import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../../core/theme/app_colors.dart'; import '../shell/nav_bar_config.dart'; import '../../core/utils/date_utils.dart' as du; import '../../shared/widgets/action_bar.dart'; import '../../shared/widgets/form_section.dart'; import '../../shared/widgets/form_field_row.dart'; import '../../core/i18n/app_localizations.dart'; import 'vehicle_apply_controller.dart'; class VehicleApplyPage extends ConsumerStatefulWidget { final String? editId; const VehicleApplyPage({super.key, this.editId}); @override ConsumerState createState() => _VehicleApplyPageState(); } class _VehicleApplyPageState extends ConsumerState { static const _vehicleTypes = ['轿车', 'SUV', '商务车']; static const _purposes = ['客户接待', '商务出行', '内部公务', '其他']; @override Widget build(BuildContext context) { final ctrl = ref.watch(vehicleApplyProvider(widget.editId).notifier); final state = ref.watch(vehicleApplyProvider(widget.editId)); final l10n = AppLocalizations.of(context); ref .read(navBarConfigProvider.notifier) .update( NavBarConfig( title: l10n.get('vehicleApply'), showBack: true, onBack: () => context.pop(), ), ); return Column( children: [ Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: [ FormSection( title: '用车信息', children: [ FormFieldRow( label: '选择车辆', value: state.vehicle.vehicleType.isNotEmpty ? state.vehicle.vehicleType : null, hint: '请选择车牌', onTap: () => _showPicker( _vehicleTypes, ctrl.updateVehicleType, title: '选择车辆', ), ), // 冲突提示 Container( height: 36, padding: const EdgeInsets.symmetric(horizontal: 12), decoration: BoxDecoration( color: const Color(0xFFFFF3E0), borderRadius: BorderRadius.circular(4), ), child: Row( children: [ Icon( Icons.warning_amber_rounded, size: 14, color: AppColors.warning, ), const SizedBox(width: 8), const Text( '该车辆在此时段已被占用', style: TextStyle( fontSize: AppFontSizes.caption, color: AppColors.warning, ), ), ], ), ), FormFieldRow( label: '用车事由', value: state.vehicle.purpose.isNotEmpty ? state.vehicle.purpose : null, hint: '请选择或填写事由', onTap: () => _showPurposeDialog(ctrl), ), // 出发地点 (with map-pin icon) _buildIconFieldRow( '出发地点', state.vehicle.origin, 'GPS定位中…', () => _showInput('出发地点', ctrl.updateOrigin), ), // 目的地点 (with map-pin icon) _buildIconFieldRow( '目的地点', state.vehicle.destination, '请输入目的地点', () => _showInput('目的地点', ctrl.updateDestination), ), FormFieldRow( label: '乘车人数', value: state.vehicle.passengerCount > 0 ? '${state.vehicle.passengerCount}人' : null, hint: '请选择', onTap: () => _showNumberInput( '乘车人数', ctrl.updatePassengerCount, state.vehicle.passengerCount, ), ), FormFieldRow( label: '开始时间', value: du.DateUtils.formatDateTime( state.vehicle.startTime, ), onTap: () => _pickDateTime( ctrl.updateStartTime, state.vehicle.startTime, ), ), FormFieldRow( label: '结束时间', value: du.DateUtils.formatDateTime(state.vehicle.endTime), onTap: () => _pickDateTime( ctrl.updateEndTime, state.vehicle.endTime, ), ), ], ), ], ), ), ), ActionBar( leftLabel: '重置', centerLabel: '存草稿', rightLabel: '提交审批', showLeft: true, onLeftTap: null, onCenterTap: state.isSubmitting ? null : () async { await ctrl.saveDraft(); if (context.mounted) context.pop(); }, onRightTap: state.isSubmitting ? null : () async { final ok = await ctrl.submit(); if (context.mounted && ok) context.pop(); }, ), ], ); } Widget _buildIconFieldRow( String label, String value, String hint, VoidCallback onTap, ) { final hasValue = value.isNotEmpty; return GestureDetector( onTap: onTap, child: Container( height: 44, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: const TextStyle( fontSize: AppFontSizes.body, color: AppColors.textSecondary, ), ), Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.location_on, size: 14, color: AppColors.primary), const SizedBox(width: 4), Text( hasValue ? value : hint, style: TextStyle( fontSize: AppFontSizes.body, color: hasValue ? AppColors.textPrimary : AppColors.textPlaceholder, ), ), const SizedBox(width: 4), const Icon( Icons.chevron_right, size: 14, color: AppColors.textPlaceholder, ), ], ), ], ), ), ); } void _showPicker( List options, void Function(String) onPick, { String title = '请选择', }) { TDPicker.showMultiPicker( context, title: title, data: [options], onConfirm: (selected) => onPick(selected.first), ); } void _showPurposeDialog(VehicleApplyController ctrl) { TDPicker.showMultiPicker( context, title: '选择用车事由', data: [_purposes], onConfirm: (selected) { ctrl.updatePurpose(selected.first); // 如果选择"其他",弹出文本输入 if (selected.first == '其他') { _showInput('用车事由', ctrl.updatePurpose); } }, ); } void _showInput(String title, void Function(String) onSave) { final ctrl = TextEditingController(); showDialog( context: context, builder: (_) => TDAlertDialog( title: title, contentWidget: TextField( controller: ctrl, decoration: InputDecoration( hintText: '请输入$title', border: const OutlineInputBorder(), ), ), leftBtn: TDDialogButtonOptions( title: '取消', action: () => Navigator.pop(context), ), rightBtn: TDDialogButtonOptions( title: '确定', theme: TDButtonTheme.primary, action: () { onSave(ctrl.text); Navigator.pop(context); }, ), ), ); } void _showNumberInput(String title, void Function(int) onSave, int current) { final ctrl = TextEditingController(text: '$current'); showDialog( context: context, builder: (_) => TDAlertDialog( title: title, contentWidget: TextField( controller: ctrl, keyboardType: TextInputType.number, decoration: const InputDecoration(border: OutlineInputBorder()), ), leftBtn: TDDialogButtonOptions( title: '取消', action: () => Navigator.pop(context), ), rightBtn: TDDialogButtonOptions( title: '确定', theme: TDButtonTheme.primary, action: () { onSave(int.tryParse(ctrl.text) ?? 1); Navigator.pop(context); }, ), ), ); } void _pickDateTime(void Function(DateTime) onPicked, DateTime initial) { TDPicker.showDatePicker( context, title: '选择日期时间', useYear: true, useMonth: true, useDay: true, useHour: true, useMinute: true, initialDate: [ initial.year, initial.month, initial.day, initial.hour, initial.minute, ], onConfirm: (selected) { onPicked( DateTime( selected['year']!, selected['month']!, selected['day']!, selected['hour']!, selected['minute']!, ), ); }, ); } }