|
|
@@ -19,6 +19,7 @@ import '../../shared/helpers/audit_flow_helper.dart';
|
|
|
import '../../core/theme/app_colors.dart';
|
|
|
import '../../core/theme/app_colors_extension.dart';
|
|
|
import 'overtime_apply_api.dart';
|
|
|
+import 'overtime_apply_model.dart';
|
|
|
import '../expense_apply/expense_apply_api.dart';
|
|
|
import 'widgets/overtime_apply_detail_dialog.dart';
|
|
|
|
|
|
@@ -269,86 +270,103 @@ class _OvertimeApplyCreatePageState
|
|
|
Future<void> _restoreDraft() async {
|
|
|
final data = await DraftStorage.load(_draftKey);
|
|
|
if (data == null) return;
|
|
|
- setState(() {
|
|
|
- _reasonController.text = data['reason'] as String? ?? '';
|
|
|
- _remarkController.text = data['remark'] as String? ?? '';
|
|
|
- _selectedDeptId = data['deptId'] as String? ?? '';
|
|
|
- _selectedDeptName = data['deptName'] as String? ?? '';
|
|
|
- _selectedApplicantId = data['applicantId'] as String? ?? '';
|
|
|
- _selectedApplicantName = data['applicantName'] as String? ?? '';
|
|
|
- _details.clear();
|
|
|
- final detailList = data['details'] as List<dynamic>?;
|
|
|
- if (detailList != null) {
|
|
|
- for (final d in detailList) {
|
|
|
- final m = d as Map<String, dynamic>;
|
|
|
+ try {
|
|
|
+ final model = OvertimeApplyModel.fromJson(data);
|
|
|
+ setState(() {
|
|
|
+ _reasonController.text = model.reason;
|
|
|
+ _remarkController.text = model.rem;
|
|
|
+ _selectedDeptId = model.dep;
|
|
|
+ _selectedDeptName = model.depName;
|
|
|
+ _selectedApplicantId = model.salNo;
|
|
|
+ _selectedApplicantName = model.salName;
|
|
|
+ _details.clear();
|
|
|
+ for (final d in model.details) {
|
|
|
_details.add(
|
|
|
_DetailItem(
|
|
|
- id: m['id'] as int? ?? _detailIdCounter++,
|
|
|
- jbNo: m['jbNo'] as String?,
|
|
|
- itm: m['itm'] as int? ?? 0,
|
|
|
- salNo: m['salNo'] as String? ?? '',
|
|
|
- salName: m['salName'] as String? ?? '',
|
|
|
- dep: m['dep'] as String? ?? '',
|
|
|
- depName: m['depName'] as String? ?? '',
|
|
|
- jbType: m['jbType'] as String? ?? 'WORKING_DAY',
|
|
|
- jbDate: m['jbDate'] as String? ?? '',
|
|
|
- startTime: m['startTime'] as String? ?? '',
|
|
|
- endTime: m['endTime'] as String? ?? '',
|
|
|
- jbHours: (m['jbHours'] as num?)?.toDouble() ?? 0.0,
|
|
|
- jbDays: (m['jbDays'] as num?)?.toDouble() ?? 0.0,
|
|
|
- attPeriod: m['attPeriod'] as String? ?? '',
|
|
|
- reason: m['detailReason'] as String? ?? '',
|
|
|
- compensationType:
|
|
|
- m['compensationType'] as String? ?? 'OVERTIME_PAY',
|
|
|
- compensationCount:
|
|
|
- (m['compensationCount'] as num?)?.toDouble() ?? 0.0,
|
|
|
- adr: m['adr'] as String? ?? '',
|
|
|
- rem: m['rem'] as String? ?? '',
|
|
|
+ id: _detailIdCounter++,
|
|
|
+ jbNo: d.jbNo,
|
|
|
+ itm: d.itm,
|
|
|
+ salNo: d.salNo,
|
|
|
+ salName: d.salName,
|
|
|
+ dep: d.dep,
|
|
|
+ depName: d.depName,
|
|
|
+ jbType: d.jbType,
|
|
|
+ jbDate: d.jbDate != null
|
|
|
+ ? '${d.jbDate!.year}-${d.jbDate!.month.toString().padLeft(2, '0')}-${d.jbDate!.day.toString().padLeft(2, '0')}'
|
|
|
+ : '',
|
|
|
+ startTime: d.startTime != null
|
|
|
+ ? '${d.startTime!.year}-${d.startTime!.month.toString().padLeft(2, '0')}-${d.startTime!.day.toString().padLeft(2, '0')} '
|
|
|
+ '${d.startTime!.hour.toString().padLeft(2, '0')}:${d.startTime!.minute.toString().padLeft(2, '0')}'
|
|
|
+ : '',
|
|
|
+ endTime: d.endTime != null
|
|
|
+ ? '${d.endTime!.year}-${d.endTime!.month.toString().padLeft(2, '0')}-${d.endTime!.day.toString().padLeft(2, '0')} '
|
|
|
+ '${d.endTime!.hour.toString().padLeft(2, '0')}:${d.endTime!.minute.toString().padLeft(2, '0')}'
|
|
|
+ : '',
|
|
|
+ jbHours: d.jbHours,
|
|
|
+ jbDays: d.jbDays,
|
|
|
+ attPeriod: d.attPeriod,
|
|
|
+ reason: d.reason,
|
|
|
+ compensationType: d.compensationType,
|
|
|
+ compensationCount: d.compensationCount,
|
|
|
+ adr: d.adr,
|
|
|
+ rem: d.rem,
|
|
|
),
|
|
|
);
|
|
|
}
|
|
|
- }
|
|
|
- _detailIdCounter = _details.isEmpty
|
|
|
- ? 1
|
|
|
- : _details.map((d) => d.id).reduce((a, b) => a > b ? a : b) + 1;
|
|
|
- });
|
|
|
+ _detailIdCounter = _details.isEmpty
|
|
|
+ ? 1
|
|
|
+ : _details.map((d) => d.id).reduce((a, b) => a > b ? a : b) + 1;
|
|
|
+ });
|
|
|
+ } catch (_) {
|
|
|
+ // 旧格式草稿兼容
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
Future<void> _saveDraftToStorage() async {
|
|
|
- final detailList = _details
|
|
|
- .map(
|
|
|
- (d) => {
|
|
|
- 'id': d.id,
|
|
|
- 'jbNo': d.jbNo,
|
|
|
- 'itm': d.itm,
|
|
|
- 'salNo': d.salNo,
|
|
|
- 'salName': d.salName,
|
|
|
- 'dep': d.dep,
|
|
|
- 'depName': d.depName,
|
|
|
- 'jbType': d.jbType,
|
|
|
- 'jbDate': d.jbDate,
|
|
|
- 'startTime': d.startTime,
|
|
|
- 'endTime': d.endTime,
|
|
|
- 'jbHours': d.jbHours,
|
|
|
- 'jbDays': d.jbDays,
|
|
|
- 'attPeriod': d.attPeriod,
|
|
|
- 'detailReason': d.reason,
|
|
|
- 'compensationType': d.compensationType,
|
|
|
- 'compensationCount': d.compensationCount,
|
|
|
- 'adr': d.adr,
|
|
|
- 'rem': d.rem,
|
|
|
- },
|
|
|
- )
|
|
|
- .toList();
|
|
|
- await DraftStorage.save(_draftKey, {
|
|
|
- 'reason': _reasonController.text,
|
|
|
- 'remark': _remarkController.text,
|
|
|
- 'deptId': _selectedDeptId,
|
|
|
- 'deptName': _selectedDeptName,
|
|
|
- 'applicantId': _selectedApplicantId,
|
|
|
- 'applicantName': _selectedApplicantName,
|
|
|
- 'details': detailList,
|
|
|
- });
|
|
|
+ final now = DateTime.now();
|
|
|
+ final model = OvertimeApplyModel(
|
|
|
+ jbNo: '',
|
|
|
+ jbDd: now,
|
|
|
+ salNo: _selectedApplicantId,
|
|
|
+ salName: _selectedApplicantName,
|
|
|
+ dep: _selectedDeptId,
|
|
|
+ depName: _selectedDeptName,
|
|
|
+ reason: _reasonController.text,
|
|
|
+ rem: _remarkController.text,
|
|
|
+ usr: HostAppChannel.usr,
|
|
|
+ recordDd: now,
|
|
|
+ details: _details
|
|
|
+ .map(
|
|
|
+ (d) => OvertimeApplyDetailModel(
|
|
|
+ itm: d.itm,
|
|
|
+ jbNo: d.jbNo,
|
|
|
+ salNo: d.salNo,
|
|
|
+ salName: d.salName,
|
|
|
+ dep: d.dep,
|
|
|
+ depName: d.depName,
|
|
|
+ jbType: d.jbType,
|
|
|
+ jbDate: d.jbDate.isNotEmpty
|
|
|
+ ? DateTime.tryParse(d.jbDate)
|
|
|
+ : null,
|
|
|
+ startTime: d.startTime.isNotEmpty
|
|
|
+ ? DateTime.tryParse(d.startTime)
|
|
|
+ : null,
|
|
|
+ endTime: d.endTime.isNotEmpty
|
|
|
+ ? DateTime.tryParse(d.endTime)
|
|
|
+ : null,
|
|
|
+ jbHours: d.jbHours,
|
|
|
+ jbDays: d.jbDays,
|
|
|
+ attPeriod: d.attPeriod,
|
|
|
+ reason: d.reason,
|
|
|
+ compensationType: d.compensationType,
|
|
|
+ compensationCount: d.compensationCount,
|
|
|
+ adr: d.adr,
|
|
|
+ rem: d.rem,
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ .toList(),
|
|
|
+ );
|
|
|
+ await DraftStorage.save(_draftKey, model.toJson());
|
|
|
}
|
|
|
|
|
|
// ═══ 草稿弹窗 ═══
|
|
|
@@ -365,7 +383,7 @@ class _OvertimeApplyCreatePageState
|
|
|
buttonStyle: TDDialogButtonStyle.text,
|
|
|
leftBtn: TDDialogButtonOptions(
|
|
|
title: l10n.get('discard'),
|
|
|
- titleColor: colors.danger,
|
|
|
+ titleColor: colors.textSecondary,
|
|
|
action: () {
|
|
|
Navigator.pop(ctx);
|
|
|
DraftStorage.delete(_draftKey);
|
|
|
@@ -525,6 +543,11 @@ class _OvertimeApplyCreatePageState
|
|
|
_detailLabel(
|
|
|
'${l10n.get('jbDate')}: ${d.jbDate}',
|
|
|
colors,
|
|
|
+ trailing: _weekdayLabel(d.jbDate, l10n) != null
|
|
|
+ ? _weekdayTag(
|
|
|
+ _weekdayLabel(d.jbDate, l10n)!,
|
|
|
+ )
|
|
|
+ : null,
|
|
|
),
|
|
|
],
|
|
|
if (d.startTime.isNotEmpty) ...[
|
|
|
@@ -661,7 +684,7 @@ class _OvertimeApplyCreatePageState
|
|
|
return dt;
|
|
|
}
|
|
|
|
|
|
- String? _dayOfWeekLabel(String dateTimeStr, AppLocalizations l10n) {
|
|
|
+ String? _weekdayLabel(String dateTimeStr, AppLocalizations l10n) {
|
|
|
final dt = DateTime.tryParse(dateTimeStr);
|
|
|
if (dt == null) return null;
|
|
|
switch (dt.weekday) {
|
|
|
@@ -699,22 +722,32 @@ class _OvertimeApplyCreatePageState
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- Widget _detailLabel(String text, AppColorsExtension colors) {
|
|
|
+ Widget _detailLabel(String text, AppColorsExtension colors, {Widget? trailing}) {
|
|
|
return Padding(
|
|
|
padding: const EdgeInsets.only(top: 2),
|
|
|
- child: Text(
|
|
|
- text,
|
|
|
- maxLines: 2,
|
|
|
- overflow: TextOverflow.ellipsis,
|
|
|
- style: TextStyle(
|
|
|
- fontSize: AppFontSizes.caption,
|
|
|
- color: colors.textSecondary,
|
|
|
- ),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ Flexible(
|
|
|
+ child: Text(
|
|
|
+ text,
|
|
|
+ maxLines: 2,
|
|
|
+ overflow: TextOverflow.ellipsis,
|
|
|
+ style: TextStyle(
|
|
|
+ fontSize: AppFontSizes.caption,
|
|
|
+ color: colors.textSecondary,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ if (trailing != null) ...[
|
|
|
+ const SizedBox(width: 6),
|
|
|
+ trailing,
|
|
|
+ ],
|
|
|
+ ],
|
|
|
),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- Widget _dayOfWeekTag(String label) {
|
|
|
+ Widget _weekdayTag(String label) {
|
|
|
final tdTheme = TDTheme.of(context);
|
|
|
return Container(
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 6),
|