226 lines
7.1 KiB
Dart
226 lines
7.1 KiB
Dart
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),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|