feat: integrate unified payment and wallet refunds

This commit is contained in:
2026-08-02 23:24:32 +08:00
parent f0b8af0bc8
commit 82a2106a97
64 changed files with 1519 additions and 254 deletions

View File

@@ -1,47 +1,172 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
import '../../../data/repositories/client_repository.dart';
import '../../../data/services/payment_launcher.dart';
import '../../../domain/models/client_models.dart';
import '../shared/record_list_page.dart';
import '../shared/record_list_view_model.dart';
class OrdersPage extends StatelessWidget {
const OrdersPage({required this.repository, super.key});
OrdersPage({required this.repository, super.key});
final ClientRepository repository;
final PaymentLauncher _launcher = PaymentLauncher();
@override
Widget build(BuildContext context) => DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: const Text('我的订单'),
bottom: const TabBar(
tabs: [
Tab(text: '商城'),
Tab(text: '供气'),
Tab(text: '服务工单'),
Future<void> _openActions(
BuildContext context,
ClientRecord record,
String business,
) async {
final action = await showModalBottomSheet<String>(
context: context,
builder: (context) => SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (record.status == 16 || record.status == 18) ...[
ListTile(
title: const Text('支付宝支付'),
onTap: () => Navigator.pop(context, 'alipay'),
),
ListTile(
title: const Text('微信支付'),
onTap: () => Navigator.pop(context, 'wechat'),
),
],
if (record.status == 18 || record.status == 35)
ListTile(
title: const Text('申请退款'),
onTap: () => Navigator.pop(context, 'refund'),
),
],
),
),
body: TabBarView(
children: [
RecordListPage(
title: '商城订单',
eyebrow: '交易',
viewModel: RecordListViewModel(repository.shopOrders),
);
if (action == null || !context.mounted) return;
try {
if (action == 'refund') {
final reason = await _reason(context);
if (reason == null || !context.mounted) return;
final rawItems = record.raw['items'] as List? ?? const [];
await repository.createRefund(
business: business,
identity: record.identity,
requestNo: const Uuid().v7(),
reason: reason,
items: rawItems.map((item) {
final value = (item as Map).cast<String, Object?>();
return <String, Object?>{
'identity': value['identity'],
'quantity': value['quantity'] ?? 1,
};
}).toList(),
);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('退款申请已提交')),
);
}
return;
}
final payment = await repository.payOrder(
business: business,
identity: record.identity,
requestNo: const Uuid().v7(),
channel: action,
payType: kIsWeb
? (action == 'alipay' ? 'wap' : 'jsapi')
: 'app',
openid: kIsWeb && action == 'wechat'
? Uri.base.queryParameters['openid'] ?? ''
: '',
);
await _launcher.launch(payment);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('支付结果确认中,请稍后刷新')),
);
}
} catch (error) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(error.toString())),
);
}
}
}
Future<String?> _reason(BuildContext context) async {
final controller = TextEditingController();
final value = await showDialog<String>(
context: context,
builder: (context) => AlertDialog(
title: const Text('申请退款'),
content: TextField(
controller: controller,
maxLines: 3,
decoration: const InputDecoration(labelText: '退款原因'),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
RecordListPage(
title: '供气订单',
eyebrow: '履约',
viewModel: RecordListViewModel(repository.gasOrders),
),
RecordListPage(
title: '服务工单',
eyebrow: '服务',
viewModel: RecordListViewModel(repository.tickets),
FilledButton(
onPressed: () => Navigator.pop(context, controller.text.trim()),
child: const Text('提交'),
),
],
),
),
);
);
controller.dispose();
return value?.isEmpty == true ? null : value;
}
@override
Widget build(BuildContext context) => DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
title: const Text('我的订单'),
bottom: const TabBar(
tabs: [
Tab(text: '商城'),
Tab(text: '供气'),
Tab(text: '退款'),
Tab(text: '工单'),
],
),
),
body: TabBarView(
children: [
RecordListPage(
title: '商城订单',
eyebrow: '交易',
viewModel: RecordListViewModel(repository.shopOrders),
onRecordTap: (record) =>
_openActions(context, record, 'shop'),
),
RecordListPage(
title: '供气订单',
eyebrow: '履约',
viewModel: RecordListViewModel(repository.gasOrders),
onRecordTap: (record) =>
_openActions(context, record, 'gas'),
),
RecordListPage(
title: '退款记录',
eyebrow: '资金',
viewModel: RecordListViewModel(repository.refunds),
),
RecordListPage(
title: '服务工单',
eyebrow: '服务',
viewModel: RecordListViewModel(repository.tickets),
),
],
),
),
);
}

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import '../../core/widgets.dart';
import '../../../domain/models/client_models.dart';
import 'record_list_view_model.dart';
class RecordListPage extends StatefulWidget {
@@ -10,6 +11,7 @@ class RecordListPage extends StatefulWidget {
required this.viewModel,
this.description,
this.floatingActionButton,
this.onRecordTap,
super.key,
});
@@ -18,6 +20,7 @@ class RecordListPage extends StatefulWidget {
final String? description;
final RecordListViewModel viewModel;
final Widget? floatingActionButton;
final ValueChanged<ClientRecord>? onRecordTap;
@override
State<RecordListPage> createState() => _RecordListPageState();
@@ -74,7 +77,10 @@ class _RecordListPageState extends State<RecordListPage> {
else
SliverList.builder(
itemCount: state.records.length,
itemBuilder: (context, index) => RecordCard(record: state.records[index]),
itemBuilder: (context, index) => RecordCard(
record: state.records[index],
onTap: widget.onRecordTap == null ? null : () => widget.onRecordTap!(state.records[index]),
),
),
const SliverPadding(padding: EdgeInsets.only(bottom: 24)),
],