317 lines
9.4 KiB
Dart
317 lines
9.4 KiB
Dart
import 'package:autosos_flutter/model/misc.dart';
|
||
import 'package:autosos_flutter/model/order.dart';
|
||
import 'package:autosos_flutter/model/payment.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';
|
||
|
||
/// 完成订单页(对应 Android FinishOrderActivity)
|
||
/// 录入费用明细 → 提交完成 → 跳转记录页
|
||
class CompletePage extends ConsumerStatefulWidget {
|
||
final int orderId;
|
||
const CompletePage({super.key, required this.orderId});
|
||
|
||
@override
|
||
ConsumerState<CompletePage> createState() => _CompletePageState();
|
||
}
|
||
|
||
class _CompletePageState extends ConsumerState<CompletePage> {
|
||
MultiState _state = MultiState.loading;
|
||
Order? _order;
|
||
final List<AmountItem> _items = [];
|
||
final TextEditingController _remarkCtrl = TextEditingController();
|
||
bool _submitting = false;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_load();
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_remarkCtrl.dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
Future<void> _load() async {
|
||
setState(() => _state = MultiState.loading);
|
||
try {
|
||
final resp = await ref.read(orderApiProvider).orderDetail(widget.orderId);
|
||
if (!mounted) return;
|
||
if (!resp.isSuccess || resp.data == null) {
|
||
setState(() => _state = MultiState.error);
|
||
return;
|
||
}
|
||
setState(() {
|
||
_order = resp.data;
|
||
// 默认添加一条基础服务费
|
||
if (_items.isEmpty) {
|
||
_items.add(AmountItem(
|
||
id: 0,
|
||
title: '基础服务费',
|
||
amount: resp.data!.amount,
|
||
type: 'income',
|
||
createdAt: DateTime.now(),
|
||
orderNo: resp.data!.orderNo,
|
||
));
|
||
}
|
||
_state = MultiState.success;
|
||
});
|
||
} catch (e) {
|
||
if (mounted) setState(() => _state = MultiState.error);
|
||
}
|
||
}
|
||
|
||
double get _total => _items.fold(0, (s, e) => s + e.amount);
|
||
|
||
Future<void> _addItem() async {
|
||
final added = await showDialog<AmountItem>(
|
||
context: context,
|
||
builder: (_) => const _AddAmountDialog(),
|
||
);
|
||
if (added != null) setState(() => _items.add(added));
|
||
}
|
||
|
||
Future<void> _submit() async {
|
||
if (_items.isEmpty) {
|
||
Toast.error('请至少添加一项费用明细');
|
||
return;
|
||
}
|
||
setState(() => _submitting = true);
|
||
EasyLoading.show(status: '提交中…');
|
||
try {
|
||
final r = await ref.read(orderServiceProvider).complete(
|
||
amountItems: _items,
|
||
remark: _remarkCtrl.text.trim().isEmpty
|
||
? null
|
||
: _remarkCtrl.text.trim(),
|
||
);
|
||
EasyLoading.dismiss();
|
||
if (r is CompleteOrderInfo) {
|
||
Toast.success('订单已完成');
|
||
if (mounted) {
|
||
// 通知服务端完成(已在 OrderService 中调用)
|
||
context.go(Routes.orderRecord);
|
||
}
|
||
}
|
||
} catch (e) {
|
||
EasyLoading.dismiss();
|
||
Toast.error('提交失败:$e');
|
||
} finally {
|
||
if (mounted) setState(() => _submitting = false);
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: AppBar(
|
||
title: const Text('完成订单'),
|
||
actions: [
|
||
if (_order != null)
|
||
Padding(
|
||
padding: const EdgeInsets.only(right: 12),
|
||
child: Center(
|
||
child: Text(
|
||
'订单 ${_order!.orderNo}',
|
||
style: const TextStyle(fontSize: 12),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
body: MultiStateView(
|
||
state: _state,
|
||
onRetry: _load,
|
||
child: _order == null
|
||
? const SizedBox.shrink()
|
||
: ListView(
|
||
padding: const EdgeInsets.all(12),
|
||
children: [
|
||
_summary(),
|
||
const SizedBox(height: 12),
|
||
_itemList(),
|
||
const SizedBox(height: 8),
|
||
OutlinedButton.icon(
|
||
onPressed: _addItem,
|
||
icon: const Icon(Icons.add),
|
||
label: const Text('添加费用项'),
|
||
),
|
||
const SizedBox(height: 12),
|
||
TextField(
|
||
controller: _remarkCtrl,
|
||
maxLines: 3,
|
||
decoration: const InputDecoration(
|
||
labelText: '备注',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
),
|
||
const SizedBox(height: 16),
|
||
Row(
|
||
children: [
|
||
const Text('合计:',
|
||
style: TextStyle(fontSize: 16)),
|
||
Text(
|
||
'¥${_total.toStringAsFixed(2)}',
|
||
style: const TextStyle(
|
||
color: Colors.red,
|
||
fontSize: 22,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 12),
|
||
ElevatedButton(
|
||
onPressed: _submitting ? null : _submit,
|
||
style: ElevatedButton.styleFrom(
|
||
backgroundColor: Colors.green,
|
||
minimumSize: const Size.fromHeight(48),
|
||
),
|
||
child: Text(_submitting ? '提交中…' : '提交完成'),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _summary() {
|
||
final o = _order!;
|
||
return Material(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.circular(8),
|
||
child: Padding(
|
||
padding: const EdgeInsets.all(12),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(o.serviceType,
|
||
style:
|
||
const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||
const SizedBox(height: 4),
|
||
if (o.address != null)
|
||
Text('地点:${o.address}',
|
||
style: TextStyle(color: Colors.grey.shade700, fontSize: 13)),
|
||
if (o.customerName != null)
|
||
Text('客户:${o.customerName!}',
|
||
style: TextStyle(color: Colors.grey.shade700, fontSize: 13)),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _itemList() {
|
||
return Material(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.circular(8),
|
||
child: Column(
|
||
children: [
|
||
for (int i = 0; i < _items.length; i++)
|
||
ListTile(
|
||
title: Text(_items[i].title),
|
||
trailing: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Text('¥${_items[i].amount.toStringAsFixed(2)}',
|
||
style: const TextStyle(
|
||
color: Colors.red,
|
||
fontWeight: FontWeight.bold,
|
||
)),
|
||
IconButton(
|
||
icon: const Icon(Icons.delete_outline,
|
||
color: Colors.red),
|
||
onPressed: () =>
|
||
setState(() => _items.removeAt(i)),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 新增费用项弹窗
|
||
class _AddAmountDialog extends StatefulWidget {
|
||
const _AddAmountDialog();
|
||
@override
|
||
State<_AddAmountDialog> createState() => _AddAmountDialogState();
|
||
}
|
||
|
||
class _AddAmountDialogState extends State<_AddAmountDialog> {
|
||
final _title = TextEditingController();
|
||
final _amount = TextEditingController();
|
||
|
||
@override
|
||
void dispose() {
|
||
_title.dispose();
|
||
_amount.dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return AlertDialog(
|
||
title: const Text('添加费用项'),
|
||
content: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
TextField(
|
||
controller: _title,
|
||
decoration: const InputDecoration(
|
||
labelText: '费用名称',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
),
|
||
const SizedBox(height: 8),
|
||
TextField(
|
||
controller: _amount,
|
||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||
decoration: const InputDecoration(
|
||
labelText: '金额',
|
||
prefixText: '¥ ',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.pop(context),
|
||
child: const Text('取消'),
|
||
),
|
||
ElevatedButton(
|
||
onPressed: () {
|
||
final t = _title.text.trim();
|
||
final a = double.tryParse(_amount.text.trim()) ?? 0;
|
||
if (t.isEmpty || a <= 0) {
|
||
Toast.error('请填写完整');
|
||
return;
|
||
}
|
||
Navigator.pop(
|
||
context,
|
||
AmountItem(
|
||
id: 0,
|
||
title: t,
|
||
amount: a,
|
||
type: 'income',
|
||
createdAt: DateTime.now(),
|
||
),
|
||
);
|
||
},
|
||
child: const Text('添加'),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
}
|