Bladeren bron

fix #19888 用车申请单重复还车优化

chengc 1 week geleden
bovenliggende
commit
4885ad1f9d

+ 4 - 0
lib/features/vehicle/vehicle_apply_detail_page.dart

@@ -169,6 +169,10 @@ class _VehicleApplyDetailPageState
                         billNo: widget.billNo,
                         odometerBegin: _data?.odometerBegin ?? 0,
                         recordDd: _data?.recordDd,
+                        existingReturnTime: _data?.returnTime,
+                        existingOdometerEnd: _data?.odometerEnd ?? 0,
+                        existingAmtn: _data?.amtn ?? 0.0,
+                        existingRemark: _data?.remark ?? '',
                         onSubmit: (returnData) async {
                           final api = ref.read(vehicleApplyApiProvider);
                           returnData['usrCheck'] = HostAppChannel.usr;

+ 115 - 13
lib/features/vehicle/widgets/return_car_dialog.dart

@@ -11,18 +11,30 @@ import '../../../core/theme/app_colors_extension.dart';
 /// 还车登记弹窗。
 ///
 /// 从底部滑出,用于登记车辆归还信息。
+/// 传入 [existingReturnTime] 等已有数据时,对应字段预填,
+/// 其中实还时间(已有值时)为只读不可修改。
 class ReturnCarDialog extends StatefulWidget {
   final String billNo;
   final int odometerBegin; // 出车里程,用于对比
   final DateTime? recordDd; // 录入日期,归还时间必须大于此值
   final Future<bool> Function(Map<String, dynamic>) onSubmit; // 提交回调,返回 true=成功
 
+  // 已有还车数据(重复还车时预填)
+  final DateTime? existingReturnTime;
+  final int existingOdometerEnd;
+  final double existingAmtn;
+  final String existingRemark;
+
   const ReturnCarDialog({
     super.key,
     required this.billNo,
     required this.odometerBegin,
     this.recordDd,
     required this.onSubmit,
+    this.existingReturnTime,
+    this.existingOdometerEnd = 0,
+    this.existingAmtn = 0.0,
+    this.existingRemark = '',
   });
 
   /// 显示弹窗。
@@ -32,6 +44,10 @@ class ReturnCarDialog extends StatefulWidget {
     required int odometerBegin,
     DateTime? recordDd,
     required Future<bool> Function(Map<String, dynamic>) onSubmit,
+    DateTime? existingReturnTime,
+    int existingOdometerEnd = 0,
+    double existingAmtn = 0.0,
+    String existingRemark = '',
   }) {
     FocusScope.of(context).unfocus();
     Navigator.push(
@@ -44,6 +60,10 @@ class ReturnCarDialog extends StatefulWidget {
           odometerBegin: odometerBegin,
           recordDd: recordDd,
           onSubmit: onSubmit,
+          existingReturnTime: existingReturnTime,
+          existingOdometerEnd: existingOdometerEnd,
+          existingAmtn: existingAmtn,
+          existingRemark: existingRemark,
         ),
       ),
     );
@@ -76,11 +96,32 @@ class _ReturnCarDialogState extends State<ReturnCarDialog> {
   void initState() {
     super.initState();
     final now = DateTime.now();
-    final minDt = widget.recordDd ?? now;
-    final initDt = now.isAfter(minDt) ? now : minDt;
-    _returnTime =
-        '${initDt.year}-${initDt.month.toString().padLeft(2, '0')}-${initDt.day.toString().padLeft(2, '0')} '
-        '${initDt.hour.toString().padLeft(2, '0')}:${initDt.minute.toString().padLeft(2, '0')}:${initDt.second.toString().padLeft(2, '0')}';
+
+    // 实还时间:已有值用已有(只读),否则默认当前时间
+    if (widget.existingReturnTime != null) {
+      final t = widget.existingReturnTime!;
+      _returnTime =
+          '${t.year}-${t.month.toString().padLeft(2, '0')}-${t.day.toString().padLeft(2, '0')} '
+          '${t.hour.toString().padLeft(2, '0')}:${t.minute.toString().padLeft(2, '0')}';
+    } else {
+      final minDt = widget.recordDd ?? now;
+      final initDt = now.isAfter(minDt) ? now : minDt;
+      _returnTime =
+          '${initDt.year}-${initDt.month.toString().padLeft(2, '0')}-${initDt.day.toString().padLeft(2, '0')} '
+          '${initDt.hour.toString().padLeft(2, '0')}:${initDt.minute.toString().padLeft(2, '0')}';
+    }
+
+    // 预填已有值
+    if (widget.existingOdometerEnd > 0) {
+      _odometerCtrl.text = '${widget.existingOdometerEnd}';
+    }
+    if (widget.existingAmtn > 0) {
+      _amtnCtrl.text = widget.existingAmtn.toStringAsFixed(2);
+    }
+    if (widget.existingRemark.isNotEmpty) {
+      _remarkCtrl.text = widget.existingRemark;
+    }
+
     _odometerFocus.addListener(() => _ensureVisible(_odometerFocus));
     _amtnFocus.addListener(() => _ensureVisible(_amtnFocus));
     _remarkFocus.addListener(() => _ensureVisible(_remarkFocus));
@@ -147,12 +188,16 @@ class _ReturnCarDialogState extends State<ReturnCarDialog> {
     setState(() => _submitting = true);
     LoadingDialog.show(context, text: l10n.get('returning'));
 
-    final ok = await widget.onSubmit({
-      'returnTime': _returnTime,
+    final submitData = <String, dynamic>{
       'odometerEnd': odometerEnd,
       'amtn': amtn,
       'remark': _remarkCtrl.text.trim(),
-    });
+    };
+    // 实还时间已有则不传(后端跳过不修改)
+    if (widget.existingReturnTime == null) {
+      submitData['returnTime'] = _returnTime;
+    }
+    final ok = await widget.onSubmit(submitData);
 
     if (!mounted) return;
     LoadingDialog.hide(context);
@@ -289,6 +334,14 @@ class _ReturnCarDialogState extends State<ReturnCarDialog> {
 
   // ── 实际归还时间 ──
   Widget _buildReturnTimePicker(AppColorsExtension colors) {
+    final hasExisting = widget.existingReturnTime != null;
+    if (hasExisting) {
+      return _readOnlyCard(
+        label: l10n.get('actualReturnTime'),
+        value: _returnTime,
+        colors: colors,
+      );
+    }
     return _datePickerCard(
       label: l10n.get('actualReturnTime'),
       value: _returnTime,
@@ -461,6 +514,56 @@ class _ReturnCarDialogState extends State<ReturnCarDialog> {
     );
   }
 
+  // ── 只读信息卡片(已有时不可编辑) ──
+  Widget _readOnlyCard({
+    required String label,
+    required String value,
+    required AppColorsExtension colors,
+  }) {
+    final tdTheme = TDTheme.of(context);
+    return 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(
+            label,
+            maxLines: 1,
+            overflow: TextOverflow.visible,
+            font: tdTheme.fontBodyLarge,
+            fontWeight: FontWeight.w400,
+            style: const TextStyle(letterSpacing: 0),
+          ),
+          const SizedBox(width: 12),
+          Expanded(
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.end,
+              children: [
+                Flexible(
+                  child: TDText(
+                    value,
+                    maxLines: 1,
+                    overflow: TextOverflow.ellipsis,
+                    font: tdTheme.fontBodyLarge,
+                    fontWeight: FontWeight.w400,
+                    textColor: tdTheme.textColorPrimary,
+                    textAlign: TextAlign.end,
+                  ),
+                ),
+                const SizedBox(width: 4),
+                Icon(Icons.lock_outline, size: 16, color: tdTheme.textColorPlaceholder),
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
   void _pickDateTime(
     String label,
     ValueChanged<String> onPick, {
@@ -507,7 +610,7 @@ class _ReturnCarDialogState extends State<ReturnCarDialog> {
       useDay: true,
       useHour: true,
       useMinute: true,
-      useSecond: true,
+      useSecond: false,
       useWeekDay: false,
       dateStart: [
         minDt.year,
@@ -515,9 +618,9 @@ class _ReturnCarDialogState extends State<ReturnCarDialog> {
         minDt.day,
         minDt.hour,
         minDt.minute,
-        minDt.second,
+        0,
       ],
-      dateEnd: [now.year + 1, 12, 31, 23, 59, 59],
+      dateEnd: [now.year + 1, 12, 31, 23, 59, 0],
       initialDate: [initYear, initMonth, initDay, initHour, initMinute, 0],
       onConfirm: (selected) {
         final year = selected['year'];
@@ -525,8 +628,7 @@ class _ReturnCarDialogState extends State<ReturnCarDialog> {
         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';
-        final second = selected['second']?.toString().padLeft(2, '0') ?? '00';
-        onPick('$year-$month-$day $hour:$minute:$second');
+        onPick('$year-$month-$day $hour:$minute');
         Navigator.of(context).pop();
       },
     );