jjsos_autosos_flutter/lib/pages/order/collection_page.dart

249 lines
8.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 'package:autosos_flutter/model/base_response.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/util/toast.dart';
import 'package:autosos_flutter/widget/qr_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fluwx/fluwx.dart';
import 'package:go_router/go_router.dart';
/// 收款页(对应 Android FinishOrderMoneyActivity
/// 三种方式:微信支付(技师端唤起收款码让客户扫)/ 现金收款 / 已扫码后回调
class CollectionPage extends ConsumerStatefulWidget {
final int orderId;
const CollectionPage({super.key, required this.orderId});
@override
ConsumerState<CollectionPage> createState() => _CollectionPageState();
}
class _CollectionPageState extends ConsumerState<CollectionPage> {
Order? _order;
final TextEditingController _amountCtrl = TextEditingController();
String _qrUrl = '';
bool _loading = false;
@override
void initState() {
super.initState();
_load();
}
@override
void dispose() {
_amountCtrl.dispose();
super.dispose();
}
Future<void> _load() async {
try {
final resp =
await ref.read(orderApiProvider).orderDetail(widget.orderId);
if (!mounted) return;
if (!resp.isSuccess || resp.data == null) {
Toast.error('订单不存在');
return;
}
setState(() {
_order = resp.data;
_amountCtrl.text = resp.data!.amount.toStringAsFixed(2);
});
} catch (e) {
Toast.error('加载失败:$e');
}
}
Future<void> _wxPay() async {
final amount = double.tryParse(_amountCtrl.text.trim());
if (amount == null || amount <= 0) {
Toast.error('请输入有效金额');
return;
}
setState(() => _loading = true);
EasyLoading.show(status: '生成中…');
try {
final BaseResponse<String> resp = await ref
.read(orderApiProvider)
.createPayQrCode(widget.orderId, amount);
if (!resp.isSuccess) {
EasyLoading.dismiss();
Toast.error(resp.message ?? '生成失败');
return;
}
setState(() => _qrUrl = resp.data ?? '');
EasyLoading.dismiss();
} catch (e) {
EasyLoading.dismiss();
Toast.error('生成失败:$e');
} finally {
if (mounted) setState(() => _loading = false);
}
}
Future<void> _wxPayNative() async {
final amount = double.tryParse(_amountCtrl.text.trim());
if (amount == null || amount <= 0) {
Toast.error('请输入有效金额');
return;
}
setState(() => _loading = true);
EasyLoading.show(status: '调起微信…');
try {
final BaseResponse<WxPay> resp = await ref
.read(orderApiProvider)
.createWxPay(widget.orderId, amount);
EasyLoading.dismiss();
if (!resp.isSuccess || resp.data == null) {
Toast.error(resp.message ?? '下单失败');
return;
}
final pay = resp.data!;
await Fluwx().pay(
which: Payment(
appId: pay.appId,
partnerId: pay.partnerId,
prepayId: pay.prepayId,
packageValue: pay.packageValue,
nonceStr: pay.nonceStr,
timestamp: int.tryParse(pay.timeStamp) ?? 0,
sign: pay.sign,
),
);
} catch (e) {
EasyLoading.dismiss();
Toast.error('调起失败:$e');
} finally {
if (mounted) setState(() => _loading = false);
}
}
Future<void> _cashPay() async {
final amount = double.tryParse(_amountCtrl.text.trim());
if (amount == null || amount <= 0) {
Toast.error('请输入有效金额');
return;
}
final ok = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('现金收款确认'),
content: Text('确认已收到客户现金 ¥${amount.toStringAsFixed(2)}'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () => Navigator.pop(ctx, true),
child: const Text('确认'),
),
],
),
);
if (ok != true) return;
setState(() => _loading = true);
try {
final resp = await ref
.read(orderApiProvider)
.confirmCashPay(widget.orderId, amount);
if (!resp.isSuccess) {
Toast.error(resp.message ?? '提交失败');
return;
}
Toast.success('已记录现金收款');
if (mounted) context.pop(true);
} catch (e) {
Toast.error('提交失败:$e');
} finally {
if (mounted) setState(() => _loading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('客户付款')),
body: _order == null
? const Center(child: CircularProgressIndicator())
: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'订单号:${_order!.orderNo}',
style: const TextStyle(fontSize: 13),
),
const SizedBox(height: 16),
TextField(
controller: _amountCtrl,
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
decoration: const InputDecoration(
labelText: '收款金额',
prefixText: '¥ ',
border: OutlineInputBorder(),
),
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
const SizedBox(height: 24),
if (_qrUrl.isNotEmpty) ...[
Center(
child: QrImageView(
data: _qrUrl,
size: 220,
),
),
const SizedBox(height: 8),
const Center(
child: Text(
'请客户扫码支付',
style: TextStyle(color: Colors.grey),
),
),
const SizedBox(height: 16),
],
ElevatedButton.icon(
onPressed: _loading ? null : _wxPay,
icon: const Icon(Icons.qr_code_2),
label: const Text('生成收款二维码'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
minimumSize: const Size.fromHeight(48),
),
),
const SizedBox(height: 8),
OutlinedButton.icon(
onPressed: _loading ? null : _wxPayNative,
icon: const Icon(Icons.wechat),
label: const Text('调起微信收款'),
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.green),
foregroundColor: Colors.green,
minimumSize: const Size.fromHeight(48),
),
),
const SizedBox(height: 8),
OutlinedButton.icon(
onPressed: _loading ? null : _cashPay,
icon: const Icon(Icons.money),
label: const Text('现金收款确认'),
style: OutlinedButton.styleFrom(
minimumSize: const Size.fromHeight(48),
),
),
],
),
),
);
}
}