| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- 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<VehicleApplyPage> createState() => _VehicleApplyPageState();
- }
- class _VehicleApplyPageState extends ConsumerState<VehicleApplyPage> {
- 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<String> 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']!,
- ),
- );
- },
- );
- }
- }
|