|
|
@@ -18,6 +18,7 @@ class OvertimeDetailData {
|
|
|
final String salNo;
|
|
|
final String salName;
|
|
|
final String dep;
|
|
|
+ final String depName;
|
|
|
final String jbType;
|
|
|
final String jbDate;
|
|
|
final String startTime;
|
|
|
@@ -37,6 +38,7 @@ class OvertimeDetailData {
|
|
|
this.salNo = '',
|
|
|
this.salName = '',
|
|
|
this.dep = '',
|
|
|
+ this.depName = '',
|
|
|
this.jbType = 'WORKING_DAY',
|
|
|
this.jbDate = '',
|
|
|
this.startTime = '',
|
|
|
@@ -45,7 +47,7 @@ class OvertimeDetailData {
|
|
|
this.jbDays = 0.0,
|
|
|
this.attPeriod = '',
|
|
|
this.reason = '',
|
|
|
- this.compensationType = 'OVERTIME_PAY',
|
|
|
+ this.compensationType = '',
|
|
|
this.compensationCount = 0.0,
|
|
|
this.adr = '',
|
|
|
this.rem = '',
|
|
|
@@ -116,7 +118,7 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
final _reasonCtrl = TextEditingController();
|
|
|
final _reasonFocus = FocusNode();
|
|
|
// 补偿类型
|
|
|
- String _compensationType = 'OVERTIME_PAY';
|
|
|
+ String _compensationType = '';
|
|
|
// 折算补偿次数
|
|
|
final _compCountCtrl = TextEditingController();
|
|
|
final _compCountFocus = FocusNode();
|
|
|
@@ -150,6 +152,11 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
|
|
|
bool get _isEdit => widget.initialData != null;
|
|
|
|
|
|
+ String _todayStr() {
|
|
|
+ final now = DateTime.now();
|
|
|
+ return '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
|
|
|
+ }
|
|
|
+
|
|
|
@override
|
|
|
void initState() {
|
|
|
super.initState();
|
|
|
@@ -158,25 +165,39 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
? EmployeeItem(salNo: d.salNo, name: d.salName)
|
|
|
: null;
|
|
|
_dep = d?.dep ?? '';
|
|
|
- _depName = d?.dep ?? '';
|
|
|
+ _depName = d?.depName ?? '';
|
|
|
_jbType = d?.jbType ?? 'WORKING_DAY';
|
|
|
_jbDate = d?.jbDate ?? '';
|
|
|
if (!_isEdit && _jbDate.isEmpty) {
|
|
|
final now = DateTime.now();
|
|
|
_jbDate = '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
|
|
|
- _attPeriod = _jbDate.substring(0, 7);
|
|
|
}
|
|
|
_startTime = d?.startTime ?? '';
|
|
|
_endTime = d?.endTime ?? '';
|
|
|
+ // 新增模式:开始/结束时间给默认值
|
|
|
+ if (!_isEdit) {
|
|
|
+ final dateStr = _jbDate.isNotEmpty ? _jbDate : _todayStr();
|
|
|
+ final weekday = DateTime.tryParse(dateStr)?.weekday ?? DateTime.now().weekday;
|
|
|
+ if (_startTime.isEmpty) {
|
|
|
+ final h = (weekday <= DateTime.friday) ? 18 : 9;
|
|
|
+ _startTime = '$dateStr ${h.toString().padLeft(2, '0')}:00';
|
|
|
+ }
|
|
|
+ if (_endTime.isEmpty) {
|
|
|
+ final h = (weekday <= DateTime.friday) ? 19 : 18;
|
|
|
+ _endTime = '$dateStr ${h.toString().padLeft(2, '0')}:00';
|
|
|
+ }
|
|
|
+ _attPeriod = dateStr.substring(0, 7);
|
|
|
+ _autoCalcHours();
|
|
|
+ }
|
|
|
if (d != null) {
|
|
|
_hoursCtrl.text = d.jbHours > 0 ? d.jbHours.toString() : '';
|
|
|
_daysCtrl.text = d.jbDays > 0 ? d.jbDays.toString() : '';
|
|
|
+ _attPeriod = d.attPeriod;
|
|
|
}
|
|
|
- _attPeriod = d?.attPeriod ?? '';
|
|
|
if (d != null) {
|
|
|
_reasonCtrl.text = d.reason;
|
|
|
}
|
|
|
- _compensationType = d?.compensationType ?? 'OVERTIME_PAY';
|
|
|
+ _compensationType = d?.compensationType ?? '';
|
|
|
if (d != null) {
|
|
|
_compCountCtrl.text = d.compensationCount > 0
|
|
|
? d.compensationCount.toString()
|
|
|
@@ -187,8 +208,14 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
_remarkCtrl.text = d.rem;
|
|
|
}
|
|
|
_reasonFocus.addListener(() => _ensureVisible(_reasonFocus));
|
|
|
- _hoursFocus.addListener(() => _ensureVisible(_hoursFocus));
|
|
|
- _daysFocus.addListener(() => _ensureVisible(_daysFocus));
|
|
|
+ _hoursFocus.addListener(() {
|
|
|
+ _ensureVisible(_hoursFocus);
|
|
|
+ if (!_hoursFocus.hasFocus) _roundToHalf(_hoursCtrl);
|
|
|
+ });
|
|
|
+ _daysFocus.addListener(() {
|
|
|
+ _ensureVisible(_daysFocus);
|
|
|
+ if (!_daysFocus.hasFocus) _roundToHalf(_daysCtrl);
|
|
|
+ });
|
|
|
_compCountFocus.addListener(() => _ensureVisible(_compCountFocus));
|
|
|
_adrFocus.addListener(() => _ensureVisible(_adrFocus));
|
|
|
_remarkFocus.addListener(() => _ensureVisible(_remarkFocus));
|
|
|
@@ -317,6 +344,7 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
salNo: _selEmployee!.salNo,
|
|
|
salName: _selEmployee!.name,
|
|
|
dep: _dep,
|
|
|
+ depName: _depName,
|
|
|
jbType: _jbType,
|
|
|
jbDate: _jbDate,
|
|
|
startTime: _startTime,
|
|
|
@@ -372,10 +400,9 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
children: [
|
|
|
+ // ── 必填 ──
|
|
|
_buildEmployeePicker(colors),
|
|
|
const SizedBox(height: 12),
|
|
|
- _buildDeptPicker(colors),
|
|
|
- const SizedBox(height: 12),
|
|
|
_buildJbTypeSelector(colors),
|
|
|
const SizedBox(height: 12),
|
|
|
_buildJbDatePicker(colors),
|
|
|
@@ -383,6 +410,9 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
_buildStartTimePicker(colors),
|
|
|
const SizedBox(height: 12),
|
|
|
_buildEndTimePicker(colors),
|
|
|
+ // ── 非必填 ──
|
|
|
+ const SizedBox(height: 12),
|
|
|
+ _buildDeptPicker(colors),
|
|
|
const SizedBox(height: 12),
|
|
|
_buildHoursInput(colors),
|
|
|
const SizedBox(height: 12),
|
|
|
@@ -446,7 +476,7 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
Expanded(
|
|
|
child: Center(
|
|
|
child: Text(
|
|
|
- _l10n.get('addDetail'),
|
|
|
+ _isEdit ? _l10n.get('editOvertimeDetail') : _l10n.get('addOvertimeDetail'),
|
|
|
style: TextStyle(
|
|
|
fontSize: AppFontSizes.title,
|
|
|
fontWeight: FontWeight.w600,
|
|
|
@@ -680,6 +710,29 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ String? _dayOfWeekLabel(String dateTimeStr) {
|
|
|
+ final dt = DateTime.tryParse(dateTimeStr);
|
|
|
+ if (dt == null) return null;
|
|
|
+ switch (dt.weekday) {
|
|
|
+ case 1:
|
|
|
+ return _l10n.get('monday');
|
|
|
+ case 2:
|
|
|
+ return _l10n.get('tuesday');
|
|
|
+ case 3:
|
|
|
+ return _l10n.get('wednesday');
|
|
|
+ case 4:
|
|
|
+ return _l10n.get('thursday');
|
|
|
+ case 5:
|
|
|
+ return _l10n.get('friday');
|
|
|
+ case 6:
|
|
|
+ return _l10n.get('saturday');
|
|
|
+ case 7:
|
|
|
+ return _l10n.get('sunday');
|
|
|
+ default:
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// ── 4. 申请日期 ──
|
|
|
Widget _buildJbDatePicker(AppColorsExtension colors) {
|
|
|
return _datePickerCard(
|
|
|
@@ -701,6 +754,7 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
hint: _l10n.get('pleaseSelect'),
|
|
|
colors: colors,
|
|
|
required: true,
|
|
|
+ showDayOfWeek: true,
|
|
|
onPick: (d) {
|
|
|
setState(() {
|
|
|
_startTime = d;
|
|
|
@@ -727,6 +781,7 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
hint: _l10n.get('pleaseSelect'),
|
|
|
colors: colors,
|
|
|
required: true,
|
|
|
+ showDayOfWeek: true,
|
|
|
onPick: (d) {
|
|
|
setState(() {
|
|
|
_endTime = d;
|
|
|
@@ -746,61 +801,59 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
|
|
|
void _autoCalcHours() {
|
|
|
if (_startTime.isEmpty || _endTime.isEmpty) return;
|
|
|
- try {
|
|
|
- final stParts = _startTime.split(' ');
|
|
|
- final etParts = _endTime.split(' ');
|
|
|
- if (stParts.length < 2 || etParts.length < 2) return;
|
|
|
- final stTime = stParts[1];
|
|
|
- final etTime = etParts[1];
|
|
|
- final st = _parseTime(stTime);
|
|
|
- final et = _parseTime(etTime);
|
|
|
- if (st == null || et == null) return;
|
|
|
- double hours = et - st;
|
|
|
- if (hours < 0) hours += 24.0;
|
|
|
- if (hours > 0) {
|
|
|
- // 按0.5半小时模式向上取整
|
|
|
- final rounded = (hours * 2).ceil() / 2.0;
|
|
|
- _hoursCtrl.text = rounded.toStringAsFixed(1);
|
|
|
- }
|
|
|
- _autoCalcDays();
|
|
|
- } catch (_) {}
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (totalHours > 0) {
|
|
|
+ _hoursCtrl.text = ((totalHours * 2).ceil() / 2.0).toStringAsFixed(1);
|
|
|
+ }
|
|
|
+ _autoCalcDays();
|
|
|
}
|
|
|
|
|
|
void _autoCalcDays() {
|
|
|
- if (_startTime.isEmpty || _endTime.isEmpty) return;
|
|
|
- try {
|
|
|
- final stDateStr = _startTime.split(' ').first;
|
|
|
- final etDateStr = _endTime.split(' ').first;
|
|
|
- final stDate = DateTime.tryParse(stDateStr);
|
|
|
- final etDate = DateTime.tryParse(etDateStr);
|
|
|
- if (stDate == null || etDate == null) return;
|
|
|
- final diffDays = etDate.difference(stDate).inDays;
|
|
|
- if (diffDays < 0) return;
|
|
|
- // 按0.5半天模式向上取整
|
|
|
- final ceilDays = (diffDays * 2).ceil() / 2.0;
|
|
|
- _daysCtrl.text = ceilDays.toStringAsFixed(1);
|
|
|
- } catch (_) {}
|
|
|
+ final hours = double.tryParse(_hoursCtrl.text) ?? 0;
|
|
|
+ if (hours <= 0) return;
|
|
|
+ // 8小时工作制:天数 = 小时 ÷ 8
|
|
|
+ final diffDays = hours / 8.0;
|
|
|
+ _daysCtrl.text = ((diffDays * 2).ceil() / 2.0).toStringAsFixed(1);
|
|
|
}
|
|
|
|
|
|
- double? _parseTime(String time) {
|
|
|
- final parts = time.split(':');
|
|
|
- if (parts.length < 2) return null;
|
|
|
- final h = double.tryParse(parts[0]);
|
|
|
- final m = double.tryParse(parts[1]);
|
|
|
- if (h == null || m == null) return null;
|
|
|
- return h + m / 60.0;
|
|
|
+ /// 手动输入时失焦自动按 0.5 向上取整
|
|
|
+ void _roundToHalf(TextEditingController controller) {
|
|
|
+ final text = controller.text.trim();
|
|
|
+ if (text.isEmpty) return;
|
|
|
+ final value = double.tryParse(text);
|
|
|
+ if (value == null || value <= 0) return;
|
|
|
+ final rounded = (value * 2).ceil() / 2.0;
|
|
|
+ final formatted = rounded.toStringAsFixed(1);
|
|
|
+ if (controller.text != formatted) {
|
|
|
+ controller.text = formatted;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// ── 6. 加班时长 ──
|
|
|
Widget _buildHoursInput(AppColorsExtension colors) {
|
|
|
return _inputCard(
|
|
|
label: _l10n.get('overtimeHours'),
|
|
|
- required: true,
|
|
|
+ required: false,
|
|
|
controller: _hoursCtrl,
|
|
|
hintText: '>0',
|
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
|
inputFormatters: [
|
|
|
- FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,1}$')),
|
|
|
+ FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?[05]?$')),
|
|
|
],
|
|
|
focusNode: _hoursFocus,
|
|
|
);
|
|
|
@@ -815,7 +868,7 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
hintText: '0',
|
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
|
inputFormatters: [
|
|
|
- FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}$')),
|
|
|
+ FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?[05]?$')),
|
|
|
],
|
|
|
focusNode: _daysFocus,
|
|
|
);
|
|
|
@@ -841,7 +894,7 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
return TDTextarea(
|
|
|
controller: _reasonCtrl,
|
|
|
focusNode: _reasonFocus,
|
|
|
- label: _l10n.get('overtimeReason'),
|
|
|
+ label: _l10n.get('overtimeDetailReason'),
|
|
|
hintText: _l10n.get('enterReason'),
|
|
|
maxLines: 3,
|
|
|
minLines: 1,
|
|
|
@@ -858,11 +911,12 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
|
|
|
// ── 10. 补偿类型 ──
|
|
|
Widget _buildCompensationTypeSelector(AppColorsExtension colors) {
|
|
|
- final selLabel = _compensationTypeLabel(_compensationType);
|
|
|
+ final hasValue = _compensationType.isNotEmpty;
|
|
|
+ final selLabel = hasValue ? _compensationTypeLabel(_compensationType) : _l10n.get('pleaseSelect');
|
|
|
return _pickerCard(
|
|
|
label: _l10n.get('compensationType'),
|
|
|
- required: true,
|
|
|
- hasValue: true,
|
|
|
+ required: false,
|
|
|
+ hasValue: hasValue,
|
|
|
currentLabel: selLabel,
|
|
|
onTap: () => _showCompensationTypePicker(),
|
|
|
);
|
|
|
@@ -881,7 +935,16 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
if (selected.isNotEmpty) {
|
|
|
final idx = selected.first is int ? selected.first as int : 0;
|
|
|
if (idx >= 0 && idx < _compensationTypeOptions.length) {
|
|
|
- setState(() => _compensationType = _compensationTypeOptions[idx]);
|
|
|
+ final newType = _compensationTypeOptions[idx];
|
|
|
+ setState(() {
|
|
|
+ _compensationType = newType;
|
|
|
+ // 无补偿 → 清空;其他 → 默认取 JB_DAYS
|
|
|
+ if (newType == 'NO_COMPENSATION') {
|
|
|
+ _compCountCtrl.clear();
|
|
|
+ } else if (_compCountCtrl.text.isEmpty && _daysCtrl.text.isNotEmpty) {
|
|
|
+ _compCountCtrl.text = _daysCtrl.text;
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
Navigator.of(context).pop();
|
|
|
@@ -906,6 +969,46 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
|
|
|
// ── 11. 折算补偿次数 ──
|
|
|
Widget _buildCompCountInput(AppColorsExtension colors) {
|
|
|
+ // 无补偿时显示空
|
|
|
+ if (_compensationType == 'NO_COMPENSATION') {
|
|
|
+ final tdTheme = TDTheme.of(context);
|
|
|
+ return GestureDetector(
|
|
|
+ onTap: () {},
|
|
|
+ child: Container(
|
|
|
+ padding: const EdgeInsets.only(
|
|
|
+ left: 16, right: 10, top: 12, bottom: 12,
|
|
|
+ ),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: tdTheme.bgColorContainer,
|
|
|
+ borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
|
|
|
+ border: Border.all(color: tdTheme.componentStrokeColor),
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ TDText(
|
|
|
+ _l10n.get('compensationCount'),
|
|
|
+ maxLines: 1,
|
|
|
+ overflow: TextOverflow.visible,
|
|
|
+ font: tdTheme.fontBodyLarge,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ style: const TextStyle(letterSpacing: 0),
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 12),
|
|
|
+ Expanded(
|
|
|
+ child: TDText(
|
|
|
+ '',
|
|
|
+ maxLines: 1,
|
|
|
+ textAlign: TextAlign.end,
|
|
|
+ font: tdTheme.fontBodyLarge,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ textColor: tdTheme.textColorPlaceholder,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
return _inputCard(
|
|
|
label: _l10n.get('compensationCount'),
|
|
|
required: false,
|
|
|
@@ -989,11 +1092,13 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
required ValueChanged<String> onPick,
|
|
|
VoidCallback? onClear,
|
|
|
bool required = false,
|
|
|
+ bool showDayOfWeek = false,
|
|
|
}) {
|
|
|
final tdTheme = TDTheme.of(context);
|
|
|
final hasValue = value.isNotEmpty;
|
|
|
+ final dayOfWeek = showDayOfWeek && hasValue ? _dayOfWeekLabel(value) : null;
|
|
|
return GestureDetector(
|
|
|
- onTap: () => _pickDateTime(label, onPick),
|
|
|
+ onTap: () => _pickDateTime(label, onPick, currentValue: value),
|
|
|
child: Container(
|
|
|
padding: const EdgeInsets.only(
|
|
|
left: 16,
|
|
|
@@ -1008,9 +1113,17 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
),
|
|
|
child: Row(
|
|
|
children: [
|
|
|
+ TDText(
|
|
|
+ label,
|
|
|
+ maxLines: 1,
|
|
|
+ overflow: TextOverflow.visible,
|
|
|
+ font: tdTheme.fontBodyLarge,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ style: const TextStyle(letterSpacing: 0),
|
|
|
+ ),
|
|
|
if (required)
|
|
|
Padding(
|
|
|
- padding: const EdgeInsets.only(right: 2),
|
|
|
+ padding: const EdgeInsets.only(left: 4),
|
|
|
child: TDText(
|
|
|
'*',
|
|
|
font: tdTheme.fontBodyLarge,
|
|
|
@@ -1018,14 +1131,6 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
style: TextStyle(color: tdTheme.errorColor6),
|
|
|
),
|
|
|
),
|
|
|
- TDText(
|
|
|
- label,
|
|
|
- maxLines: 1,
|
|
|
- overflow: TextOverflow.visible,
|
|
|
- font: tdTheme.fontBodyLarge,
|
|
|
- fontWeight: FontWeight.w400,
|
|
|
- style: const TextStyle(letterSpacing: 0),
|
|
|
- ),
|
|
|
const SizedBox(width: 12),
|
|
|
Expanded(
|
|
|
child: Row(
|
|
|
@@ -1045,6 +1150,25 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
textAlign: TextAlign.end,
|
|
|
),
|
|
|
),
|
|
|
+ if (dayOfWeek != null) ...[
|
|
|
+ const SizedBox(width: 6),
|
|
|
+ Container(
|
|
|
+ padding: const EdgeInsets.symmetric(
|
|
|
+ horizontal: 6,
|
|
|
+ vertical: 2,
|
|
|
+ ),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: tdTheme.brandColor1,
|
|
|
+ borderRadius: BorderRadius.circular(4),
|
|
|
+ ),
|
|
|
+ child: TDText(
|
|
|
+ dayOfWeek,
|
|
|
+ font: TDTheme.of(context).fontBodySmall,
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ textColor: tdTheme.brandColor7,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
const SizedBox(width: 4),
|
|
|
SizedBox(
|
|
|
width: 18,
|
|
|
@@ -1073,13 +1197,63 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- void _pickDateTime(String label, ValueChanged<String> onPick) {
|
|
|
+ void _pickDateTime(String label, ValueChanged<String> onPick,
|
|
|
+ {String currentValue = ''}) {
|
|
|
final l10n = _l10n;
|
|
|
final colors = Theme.of(context).extension<AppColorsExtension>()!;
|
|
|
final now = DateTime.now();
|
|
|
final isDateOnly =
|
|
|
label == _l10n.get('applyDate') || label == _l10n.get('attPeriod');
|
|
|
final isAttPeriod = label == _l10n.get('attPeriod');
|
|
|
+ final isStartTime = label == _l10n.get('startTime');
|
|
|
+ final isEndTime = label == _l10n.get('endTime');
|
|
|
+
|
|
|
+ // 根据当前值定位初始日期,无值时默认当月/当天
|
|
|
+ int initYear = now.year;
|
|
|
+ int initMonth = now.month;
|
|
|
+ int initDay = now.day;
|
|
|
+ int initHour = now.hour;
|
|
|
+ int initMinute = now.minute;
|
|
|
+
|
|
|
+ if (currentValue.isNotEmpty) {
|
|
|
+ if (isAttPeriod) {
|
|
|
+ // yyyy-MM
|
|
|
+ final parts = currentValue.split('-');
|
|
|
+ if (parts.length == 2) {
|
|
|
+ initYear = int.tryParse(parts[0]) ?? now.year;
|
|
|
+ initMonth = int.tryParse(parts[1]) ?? now.month;
|
|
|
+ initDay = 1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // yyyy-MM-dd 或 yyyy-MM-dd HH:mm
|
|
|
+ final dt = DateTime.tryParse(currentValue);
|
|
|
+ if (dt != null) {
|
|
|
+ initYear = dt.year;
|
|
|
+ initMonth = dt.month;
|
|
|
+ initDay = dt.day;
|
|
|
+ initHour = dt.hour;
|
|
|
+ initMinute = dt.minute;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } 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;
|
|
|
FocusManager.instance.primaryFocus?.unfocus();
|
|
|
TDPicker.showDatePicker(
|
|
|
context,
|
|
|
@@ -1088,13 +1262,17 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
useYear: true,
|
|
|
useMonth: true,
|
|
|
useDay: !isAttPeriod,
|
|
|
- useHour: !isDateOnly,
|
|
|
- useMinute: !isDateOnly,
|
|
|
+ useHour: useTime,
|
|
|
+ useMinute: useTime,
|
|
|
useSecond: false,
|
|
|
useWeekDay: false,
|
|
|
- dateStart: const [2020, 1, 1],
|
|
|
- dateEnd: [now.year + 1, 12, 31],
|
|
|
- initialDate: [now.year, now.month, now.day],
|
|
|
+ dateStart: useTime ? const [2020, 1, 1, 0, 0] : const [2020, 1, 1],
|
|
|
+ dateEnd: useTime
|
|
|
+ ? [now.year + 1, 12, 31, 23, 59]
|
|
|
+ : [now.year + 1, 12, 31],
|
|
|
+ initialDate: useTime
|
|
|
+ ? [initYear, initMonth, initDay, initHour, initMinute]
|
|
|
+ : [initYear, initMonth, initDay],
|
|
|
onConfirm: (selected) {
|
|
|
final year = selected['year'];
|
|
|
final month = selected['month'].toString().padLeft(2, '0');
|