jjsos_autosos_flutter/lib/pages/order/waiting_page.dart

226 lines
7.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'dart:async';
import 'package:autosos_flutter/model/order.dart';
import 'package:autosos_flutter/provider/providers.dart';
import 'package:autosos_flutter/router/app_router.dart';
import 'package:autosos_flutter/util/toast.dart';
import 'package:autosos_flutter/widget/multi_state_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
/// 等待接单页(对应 Android WaitOrderActivity
/// 列出当前可抢订单,倒计时 0 后自动从列表移除
class WaitingPage extends ConsumerStatefulWidget {
const WaitingPage({super.key});
@override
ConsumerState<WaitingPage> createState() => _WaitingPageState();
}
class _WaitingPageState extends ConsumerState<WaitingPage> {
MultiState _state = MultiState.loading;
List<WaitingOrder> _items = [];
Timer? _ticker;
@override
void initState() {
super.initState();
_load();
// 每秒刷新倒计时
_ticker = Timer.periodic(const Duration(seconds: 1), (_) {
if (mounted) setState(() {});
});
}
@override
void dispose() {
_ticker?.cancel();
super.dispose();
}
Future<void> _load() async {
setState(() => _state = MultiState.loading);
try {
final resp = await ref.read(orderApiProvider).waitingList();
if (!mounted) return;
if (!resp.isSuccess) {
setState(() => _state = MultiState.error);
Toast.error(resp.message ?? '加载失败');
return;
}
setState(() {
_items = resp.data ?? [];
_state = _items.isEmpty ? MultiState.empty : MultiState.success;
});
} catch (e) {
if (mounted) setState(() => _state = MultiState.error);
}
}
Future<void> _grab(WaitingOrder item) async {
EasyLoading.show(status: '抢单中…');
try {
final order = await ref.read(orderServiceProvider).grab(item.orderId);
EasyLoading.dismiss();
Toast.success('接单成功');
if (!mounted) return;
// 跳转到救援中页
context.pushReplacement('${Routes.orderConduct}/${order.id}');
} catch (e) {
EasyLoading.dismiss();
Toast.error('抢单失败:$e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('等待接单'),
actions: [
IconButton(icon: const Icon(Icons.refresh), onPressed: _load),
],
),
body: RefreshIndicator(
onRefresh: _load,
child: MultiStateView(
state: _state,
emptyText: '当前没有可接订单',
onRetry: _load,
child: ListView.separated(
padding: const EdgeInsets.all(12),
itemCount: _items.length,
separatorBuilder: (_, __) => const SizedBox(height: 8),
itemBuilder: (ctx, i) {
final o = _items[i];
return _WaitingCard(
item: o,
onGrab: () => _grab(o),
);
},
),
),
),
);
}
}
class _WaitingCard extends StatelessWidget {
final WaitingOrder item;
final VoidCallback onGrab;
const _WaitingCard({required this.item, required this.onGrab});
String _formatCountdown(int s) {
if (s <= 0) return '已超时';
final m = (s ~/ 60).toString().padLeft(2, '0');
final sec = (s % 60).toString().padLeft(2, '0');
return '$m:$sec';
}
@override
Widget build(BuildContext context) {
final fmt = DateFormat('MM-dd HH:mm');
final expired = item.countdown <= 0;
return Material(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(4),
),
child: Text(
item.serviceType,
style: TextStyle(
color: Colors.red.shade700,
fontSize: 12,
),
),
),
const SizedBox(width: 8),
if (item.distance != null)
Text(
'距离 ${(item.distance! / 1000).toStringAsFixed(1)} km',
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: expired ? Colors.grey : Colors.orange,
borderRadius: BorderRadius.circular(4),
),
child: Text(
_formatCountdown(item.countdown),
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 8),
Text('订单号:${item.orderNo}',
style: const TextStyle(fontSize: 13)),
if (item.address != null && item.address!.isNotEmpty) ...[
const SizedBox(height: 4),
Text('地点:${item.address}',
style: TextStyle(
fontSize: 13, color: Colors.grey.shade700)),
],
const SizedBox(height: 4),
Text('时间:${fmt.format(item.createdAt)}',
style: TextStyle(fontSize: 12, color: Colors.grey.shade500)),
const SizedBox(height: 8),
Row(
children: [
Text(
'¥${item.amount.toStringAsFixed(2)}',
style: const TextStyle(
color: Colors.red,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
ElevatedButton.icon(
onPressed: expired ? null : onGrab,
icon: const Icon(Icons.flash_on, size: 18),
label: const Text('立即抢单'),
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
),
],
),
],
),
),
);
}
}