flutter/lib/pages/order/detail.dart
2025-09-12 15:45:53 +08:00

698 lines
25 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

library;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:loopin/service/http.dart';
import 'package:loopin/api/shop_api.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
import 'package:timer_count_down/timer_count_down.dart';
import 'package:loopin/utils/wxsdk.dart';
import '../../behavior/custom_scroll_behavior.dart';
import '../../utils/lifecycle_handler.dart';
class OrderDetail extends StatefulWidget {
const OrderDetail({super.key});
@override
State<OrderDetail> createState() => _OrderDetailState();
}
class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStateMixin {
late String _orderId;
dynamic orderGoodsInfo;
int _initialSeconds = 30 * 60; // 存储初始秒数
bool _countdownFinished = false; // 新增标志位,用于跟踪倒计时是否结束
bool _isLoading = true; // 添加加载状态
@override
void initState() {
super.initState();
_orderId = Get.arguments['orderId'] ?? '';
getOrderDetail(orderId: _orderId);
LifecycleHandler.onAppResumed = _onAppResumed;
}
@override
void dispose() {
LifecycleHandler.onAppResumed = null;
super.dispose();
}
void _onAppResumed() {
print('App回到前台刷新订单状态,订单Id${_orderId}');
getOrderDetail(orderId: _orderId); // 刷新订单详情数据
_showPaymentResultDialog(); // 展示支付结果弹框
}
// 获取订单状态
void getOrderRealStatus({required String orderId}) async {
try {
final res = await Http.get('${ShopApi.goodsOrderStatus}/$orderId');
Get.toNamed('/sellerOrder');
} catch (e) {
print('报错-------------->${e}');
}
}
// 获取订单详情信息,包含商品参数
void getOrderDetail({required String orderId}) async {
try {
setState(() {
_isLoading = true;
});
final res = await Http.get('${ShopApi.goodsOrderDetail}/$orderId');
print('订单详情-------------->${res['data']}');
setState(() {
orderGoodsInfo = res['data'];
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
});
MyDialog.toast('获取订单详情失败');
}
}
// 取消订单
void _cancelOrder() async {
try {
final res = await Http.post('${ShopApi.cancelGoodsOrder}/$_orderId');
getOrderDetail(orderId: _orderId); // 刷新订单详情数据
} catch (e) {
MyDialog.toast('取消订单失败');
}
}
// 显示支付结果弹框
void _showPaymentResultDialog() {
showDialog(
context: context,
barrierDismissible: false,
barrierColor: Colors.black54,
builder: (BuildContext context) {
return Dialog(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 图标
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Color(0xFFFFF8E6),
shape: BoxShape.circle,
),
child: Icon(
Icons.payment,
size: 32,
color: Color(0xFFFFA500),
),
),
SizedBox(height: 16),
// 标题
Text(
'支付确认',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
SizedBox(height: 8),
// 描述
Text(
'请确认您的支付状态',
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
textAlign: TextAlign.center,
),
SizedBox(height: 24),
// 按钮区域
Row(
children: [
// 支付遇到问题按钮
Expanded(
child: OutlinedButton(
onPressed: () {
Navigator.of(context).pop();
getOrderRealStatus(orderId: _orderId); // 主动再次拉取订单状态
},
style: OutlinedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.grey[700],
side: BorderSide(color: Colors.grey[300]!),
padding: EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text('支付遇到问题'),
),
),
SizedBox(width: 12),
// 支付完成按钮
Expanded(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
getOrderRealStatus(orderId: _orderId); // 同时主动拉取订单状态
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFFFF5000),
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text('支付完成'),
),
),
],
),
],
),
),
);
},
);
}
// 获取订单状态文本
String getOrderStatusText(int status) {
switch (status) {
case 0:
return '待付款';
case 1:
return '待核销';
case 2:
return '已完成';
case 3:
return '已关闭';
case 4:
return '退款中';
case 5:
return '已退款';
case 6:
return '已取消';
default:
return '未知状态';
}
}
// 获取订单状态颜色
Color getOrderStatusColor(int status) {
switch (status) {
case 0:
return Colors.grey;
case 1:
return Colors.blue;
case 2:
return Colors.green;
case 3:
return Colors.red;
case 4:
return Colors.orange;
case 5:
return Colors.grey;
case 6:
return Colors.grey;
default:
return Colors.black;
}
}
// 构建商品图片
Widget _buildProductImage() {
final productInfo = orderGoodsInfo?['productInfo'][0];
final picUrl = productInfo?['pic'];
if (picUrl == null || picUrl.isEmpty) {
return Container(
width: 80.0,
height: 80.0,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8.0),
),
child: Icon(
Icons.shopping_bag_outlined,
size: 40.0,
color: Colors.grey[400],
),
);
}
return Image.network(
picUrl,
width: 80.0,
height: 80.0,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
width: 80.0,
height: 80.0,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8.0),
),
child: Icon(
Icons.shopping_bag_outlined,
size: 40.0,
color: Colors.grey[400],
),
);
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
width: 80.0,
height: 80.0,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8.0),
),
child: Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
);
}
// 构建底部按钮
Widget buildBottomButtons() {
if (orderGoodsInfo == null) return SizedBox.shrink();
int orderStatus = orderGoodsInfo?['orderStatus'] ?? 0;
switch (orderStatus) {
case 0: // 待付款
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () {
// 取消订单逻辑
_cancelOrder();
},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.white),
foregroundColor: WidgetStateProperty.all(Colors.black87),
side: WidgetStateProperty.all(BorderSide(color: Colors.grey[300]!)),
),
child: const Text('取消订单'),
),
const SizedBox(width: 10.0),
ElevatedButton(
onPressed: () {
// 打开微信小程序的某个页面地址如pages/index/index
Wxsdk.openMiniApp(orderId: _orderId);
},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Color(0xff07c160)),
foregroundColor: WidgetStateProperty.all(Colors.white),
),
child: const Text('去支付'),
),
],
);
case 1: // 待核销
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 10.0),
ElevatedButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Color(0xFFFF5000)),
foregroundColor: WidgetStateProperty.all(Colors.white),
),
child: const Text('待核销'),
),
],
);
case 2: // 已完成
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 10.0),
ElevatedButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.green),
foregroundColor: WidgetStateProperty.all(Colors.white),
),
child: const Text('已完成'),
),
],
);
case 3: // 已关闭
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 10.0),
ElevatedButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.grey),
foregroundColor: WidgetStateProperty.all(Colors.white),
),
child: const Text('已关闭'),
),
],
);
case 4: // 退款中
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 10.0),
ElevatedButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.orange),
foregroundColor: WidgetStateProperty.all(Colors.white),
),
child: const Text('退款中'),
),
],
);
case 5: // 已退款
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 10.0),
ElevatedButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.grey),
foregroundColor: WidgetStateProperty.all(Colors.white),
),
child: const Text('已退款'),
),
],
);
case 6: // 已取消
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 10.0),
ElevatedButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.grey),
foregroundColor: WidgetStateProperty.all(Colors.white),
),
child: const Text('已取消'),
),
],
);
default:
return SizedBox.shrink();
}
}
Widget emptyTip() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/empty.png',
width: 100.0,
),
SizedBox(height: 16),
Text(
'还没有订单信息~',
style: TextStyle(color: Colors.grey, fontSize: 12.0),
)
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
backgroundColor: Color(0xFFFF5000),
foregroundColor: Colors.white,
title: Text('订单详情'),
titleSpacing: 1.0,
),
body: _isLoading
? Center(child: CircularProgressIndicator())
: orderGoodsInfo == null
? emptyTip()
: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: ListView(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.all(10.0),
children: [
if (orderGoodsInfo?['orderStatus'] == 0)
Container(
padding: EdgeInsets.all(12.0),
margin: EdgeInsets.only(bottom: 10.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
offset: Offset(0.0, 1.0),
blurRadius: 1.0,
spreadRadius: 0.0,
),
],
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.info,
size: 16.0,
color: Colors.orange,
),
SizedBox(width: 4),
Text(
getOrderStatusText(orderGoodsInfo?['orderStatus']),
style: TextStyle(color: Colors.orange),
),
SizedBox(width: 4),
Text(
_countdownFinished ? '倒计时已结束' : '剩余 ',
style: TextStyle(color: Colors.grey),
),
if (!_countdownFinished)
Countdown(
seconds: _initialSeconds,
build: (_, double time) {
int m = ((time % 3600) ~/ 60).toInt();
int s = (time % 60).toInt();
String formatted = "${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}";
return Text(
formatted,
style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
);
},
interval: Duration(seconds: 1),
onFinished: () {
print("倒计时结束");
_cancelOrder();
setState(() {
_countdownFinished = true;
});
},
),
],
),
SizedBox(height: 4),
Text(
_countdownFinished
? '订单已自动取消'
: '超过30分钟未支付订单将自动取消',
style: TextStyle(color: Colors.grey, fontSize: 12.0),
),
],
),
),
// 商品信息
Container(
margin: EdgeInsets.only(bottom: 10.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
offset: Offset(0.0, 1.0),
blurRadius: 1.0,
spreadRadius: 0.0,
),
],
),
child: Column(
children: [
Row(
children: [
Spacer(),
Text(
getOrderStatusText(orderGoodsInfo?['orderStatus']),
style: TextStyle(
color: getOrderStatusColor(orderGoodsInfo?['orderStatus']),
fontWeight: FontWeight.bold,
),
)
],
),
SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildProductImage(),
SizedBox(width: 10.0),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${orderGoodsInfo?['productInfo'][0]['productName']}',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14),
),
SizedBox(height: 8),
Row(
children: [
Text(
'¥${orderGoodsInfo?['productInfo'][0]['salePrice']}',
style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
),
Spacer(),
Text(
'x${orderGoodsInfo?['productInfo'][0]['buyNum'] ?? 1}',
style: TextStyle(color: Colors.grey),
),
],
),
],
),
)
],
),
],
),
),
// 订单信息
Container(
margin: EdgeInsets.only(bottom: 10.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
offset: Offset(0.0, 1.0),
blurRadius: 1.0,
spreadRadius: 0.0,
),
],
),
child: Column(
children: [
Row(
children: [
Text(
'订单信息',
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
),
Spacer(),
InkWell(
child: Icon(
Icons.copy,
color: Colors.grey,
size: 18.0,
),
onTap: () async {
await Clipboard.setData(ClipboardData(text: _orderId));
MyDialog.toast('订单已复制到剪切板', icon: Icon(Icons.check_circle));
},
)
],
),
SizedBox(height: 10),
Column(
children: [
_buildOrderInfoRow('订单号', orderGoodsInfo?['orderId'] ?? ''),
_buildOrderInfoRow('下单时间', orderGoodsInfo?['createTime'] ?? ''),
_buildOrderInfoRow('购买数量', (orderGoodsInfo?['productInfo'][0]['buyNum'] ?? 0).toString()),
_buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['totalAmount'] ?? '0.00'}'),
_buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount'] ?? '0.00'}'),
],
)
],
),
),
],
),
),
// 底部固定按钮
bottomNavigationBar: orderGoodsInfo == null
? null
: SafeArea(
minimum: const EdgeInsets.all(10),
child: Container(
height: 60.0,
color: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: buildBottomButtons(),
),
),
);
}
Widget _buildOrderInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Row(
children: [
Text(
label,
style: TextStyle(color: Colors.grey, fontSize: 13),
),
Spacer(),
Text(value, style: TextStyle(fontSize: 13)),
],
),
);
}
}