订单联调

This commit is contained in:
cuiyouliang 2025-09-12 17:23:08 +08:00
parent c23f939c49
commit c6c6acf9dc
9 changed files with 1522 additions and 23 deletions

View File

@ -30,6 +30,9 @@ class ShopApi {
// app端我的订单 // app端我的订单
static const String myOrderList= '/app/order/page'; static const String myOrderList= '/app/order/page';
// app商家订单
static const String sellerOrderList= '/app/merchant/order/page';
// //
static const String revenueInfo= '/app/merchant/account/statistic'; static const String revenueInfo= '/app/merchant/account/statistic';

View File

@ -173,7 +173,7 @@ class _AllFunctionsPageState extends State<AllFunctionsPage> {
void _handleFunctionTap(String functionId) { void _handleFunctionTap(String functionId) {
switch (functionId) { switch (functionId) {
case 'home_order': case 'home_order':
Get.toNamed('/sellerOrder'); Get.toNamed('/myOrder');
break; break;
case 'home_balance': case 'home_balance':
showLogoutDialog(context); showLogoutDialog(context);
@ -184,6 +184,7 @@ class _AllFunctionsPageState extends State<AllFunctionsPage> {
case 'home_promo': case 'home_promo':
break; break;
case 'more_seller_order': case 'more_seller_order':
Get.toNamed('/sellerOrder');
break; break;
case 'more_seller_income': case 'more_seller_income':
Get.toNamed('/merchant/income'); Get.toNamed('/merchant/income');

View File

@ -856,7 +856,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
_buildOrderIcon('assets/images/ico_order.png', '订单', () { _buildOrderIcon('assets/images/ico_order.png', '订单', () {
Get.toNamed('/sellerOrder'); Get.toNamed('/myOrder');
}), }),
_buildOrderIcon('assets/images/ico_dhx.png', '余额logout', () { _buildOrderIcon('assets/images/ico_dhx.png', '余额logout', () {
showLogoutDialog(context); showLogoutDialog(context);

View File

@ -48,7 +48,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
void getOrderRealStatus({required String orderId}) async { void getOrderRealStatus({required String orderId}) async {
try { try {
final res = await Http.get('${ShopApi.goodsOrderStatus}/$orderId'); final res = await Http.get('${ShopApi.goodsOrderStatus}/$orderId');
Get.toNamed('/sellerOrder'); Get.toNamed('/myOrder');
} catch (e) { } catch (e) {
print('报错-------------->${e}'); print('报错-------------->${e}');
} }
@ -61,7 +61,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
_isLoading = true; _isLoading = true;
}); });
final res = await Http.get('${ShopApi.goodsOrderDetail}/$orderId'); final res = await Http.get('${ShopApi.goodsOrderDetail}/$orderId');
print('订单详情-------------->${res['data']}'); debugPrint('订单详情-------------->${res['data']}');
setState(() { setState(() {
orderGoodsInfo = res['data']; orderGoodsInfo = res['data'];
_isLoading = false; _isLoading = false;
@ -193,6 +193,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
// //
String getOrderStatusText(int status) { String getOrderStatusText(int status) {
print('111111111111$status');
switch (status) { switch (status) {
case 0: case 0:
return '待付款'; return '待付款';
@ -235,9 +236,20 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
} }
} }
//
dynamic _getFirstProductInfo() {
if (orderGoodsInfo == null ||
orderGoodsInfo['items'] == null ||
orderGoodsInfo['items'] is! List ||
orderGoodsInfo['items'].isEmpty) {
return {};
}
return orderGoodsInfo['items'][0];
}
// //
Widget _buildProductImage() { Widget _buildProductImage() {
final productInfo = orderGoodsInfo?['productInfo'][0]; final productInfo = _getFirstProductInfo();
final picUrl = productInfo?['pic']; final picUrl = productInfo?['pic'];
if (picUrl == null || picUrl.isEmpty) { if (picUrl == null || picUrl.isEmpty) {
@ -301,7 +313,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
Widget buildBottomButtons() { Widget buildBottomButtons() {
if (orderGoodsInfo == null) return SizedBox.shrink(); if (orderGoodsInfo == null) return SizedBox.shrink();
int orderStatus = orderGoodsInfo?['orderStatus'] ?? 0; int orderStatus = orderGoodsInfo?['status'] ?? 0;
switch (orderStatus) { switch (orderStatus) {
case 0: // case 0: //
@ -455,6 +467,8 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final productInfo = _getFirstProductInfo();
return Scaffold( return Scaffold(
backgroundColor: Colors.grey[50], backgroundColor: Colors.grey[50],
appBar: AppBar( appBar: AppBar(
@ -473,7 +487,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
physics: BouncingScrollPhysics(), physics: BouncingScrollPhysics(),
padding: EdgeInsets.all(10.0), padding: EdgeInsets.all(10.0),
children: [ children: [
if (orderGoodsInfo?['orderStatus'] == 0) if (orderGoodsInfo?['status'] == 0)
Container( Container(
padding: EdgeInsets.all(12.0), padding: EdgeInsets.all(12.0),
margin: EdgeInsets.only(bottom: 10.0), margin: EdgeInsets.only(bottom: 10.0),
@ -501,7 +515,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
), ),
SizedBox(width: 4), SizedBox(width: 4),
Text( Text(
getOrderStatusText(orderGoodsInfo?['orderStatus']), getOrderStatusText(orderGoodsInfo?['status']),
style: TextStyle(color: Colors.orange), style: TextStyle(color: Colors.orange),
), ),
SizedBox(width: 4), SizedBox(width: 4),
@ -565,9 +579,9 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
children: [ children: [
Spacer(), Spacer(),
Text( Text(
getOrderStatusText(orderGoodsInfo?['orderStatus']), getOrderStatusText(orderGoodsInfo?['status']),
style: TextStyle( style: TextStyle(
color: getOrderStatusColor(orderGoodsInfo?['orderStatus']), color: getOrderStatusColor(orderGoodsInfo?['status']),
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
) )
@ -584,7 +598,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'${orderGoodsInfo?['productInfo'][0]['productName']}', productInfo['productName']?.toString() ?? '商品名称未知',
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14), style: TextStyle(fontSize: 14),
@ -593,12 +607,12 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
Row( Row(
children: [ children: [
Text( Text(
'¥${orderGoodsInfo?['productInfo'][0]['salePrice']}', '¥${productInfo['salePrice']?.toString() ?? '0.00'}',
style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold), style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
), ),
Spacer(), Spacer(),
Text( Text(
'x${orderGoodsInfo?['productInfo'][0]['buyNum'] ?? 1}', 'x${productInfo['buyNum']?.toString() ?? '1'}',
style: TextStyle(color: Colors.grey), style: TextStyle(color: Colors.grey),
), ),
], ],
@ -654,7 +668,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
children: [ children: [
_buildOrderInfoRow('订单号', orderGoodsInfo?['orderId'] ?? ''), _buildOrderInfoRow('订单号', orderGoodsInfo?['orderId'] ?? ''),
_buildOrderInfoRow('下单时间', orderGoodsInfo?['createTime'] ?? ''), _buildOrderInfoRow('下单时间', orderGoodsInfo?['createTime'] ?? ''),
_buildOrderInfoRow('购买数量', (orderGoodsInfo?['productInfo'][0]['buyNum'] ?? 0).toString()), _buildOrderInfoRow('购买数量', (productInfo['buyNum'] ?? 0).toString()),
_buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['totalAmount'] ?? '0.00'}'), _buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['totalAmount'] ?? '0.00'}'),
_buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount'] ?? '0.00'}'), _buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount'] ?? '0.00'}'),
], ],

View File

@ -10,14 +10,14 @@ import 'package:loopin/service/http.dart';
import '../../behavior/custom_scroll_behavior.dart'; import '../../behavior/custom_scroll_behavior.dart';
class Seller extends StatefulWidget { class MyOrder extends StatefulWidget {
const Seller({super.key}); const MyOrder({super.key});
@override @override
State<Seller> createState() => _SellerState(); State<MyOrder> createState() => _MyOrderState();
} }
class _SellerState extends State<Seller> with SingleTickerProviderStateMixin { class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey(); GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
// - Tab单独存储数据 // - Tab单独存储数据
final int pageSize = 10; final int pageSize = 10;

View File

@ -0,0 +1,751 @@
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 SellerOrderDetail extends StatefulWidget {
const SellerOrderDetail({super.key});
@override
State<SellerOrderDetail> createState() => _SellerOrderDetailState();
}
class _SellerOrderDetailState extends State<SellerOrderDetail> 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('/myOrder');
} 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;
}
}
//
List<dynamic> getProductInfoList() {
// 使 items
if (orderGoodsInfo?['items'] != null && orderGoodsInfo!['items'] is List) {
return orderGoodsInfo!['items'];
}
// items 使 productInfo
if (orderGoodsInfo?['productInfo'] != null && orderGoodsInfo!['productInfo'] is List) {
return orderGoodsInfo!['productInfo'];
}
//
return [];
}
//
dynamic getFirstProductInfo() {
final productList = getProductInfoList();
return productList.isNotEmpty ? productList[0] : null;
}
//
Widget _buildProductImage(dynamic productInfo) {
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 _buildProductList() {
final productList = getProductInfoList();
if (productList.isEmpty) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text(
'暂无商品信息',
style: TextStyle(color: Colors.grey, fontSize: 14),
),
);
}
return Column(
children: productList.map((product) {
return Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildProductImage(product),
SizedBox(width: 10.0),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product?['productName']?.toString() ?? '未知商品',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14),
),
SizedBox(height: 8),
Row(
children: [
Text(
'¥${product?['salePrice']?.toString() ?? '0.00'}',
style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
),
Spacer(),
Text(
'x${product?['buyNum']?.toString() ?? '1'}',
style: TextStyle(color: Colors.grey),
),
],
),
],
),
)
],
),
);
}).toList(),
);
}
//
Widget buildBottomButtons() {
if (orderGoodsInfo == null) return SizedBox.shrink();
int orderStatus = orderGoodsInfo?['status'] ?? 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?['status'] == 0) // status orderStatus
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?['status'] ?? 0),
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?['status'] ?? 0),
style: TextStyle(
color: getOrderStatusColor(orderGoodsInfo?['status'] ?? 0),
fontWeight: FontWeight.bold,
),
)
],
),
SizedBox(height: 10),
_buildProductList(),
],
),
),
//
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']?.toString() ?? ''),
_buildOrderInfoRow('下单时间', orderGoodsInfo?['createTime']?.toString() ?? ''),
_buildOrderInfoRow('购买数量', _calculateTotalQuantity().toString()),
_buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['totalAmount']?.toString() ?? '0.00'}'),
_buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount']?.toString() ?? '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(),
),
),
);
}
//
int _calculateTotalQuantity() {
final productList = getProductInfoList();
int total = 0;
for (var product in productList) {
total += (product?['buyNum'] as int? ?? 0);
}
return total;
}
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)),
],
),
);
}
}

View File

@ -0,0 +1,728 @@
///
library;
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loopin/utils/wxsdk.dart';
import 'package:loopin/api/shop_api.dart';
import 'package:loopin/components/my_toast.dart';
import 'package:loopin/service/http.dart';
import '../../behavior/custom_scroll_behavior.dart';
class SellerOrder extends StatefulWidget {
const SellerOrder({super.key});
@override
State<SellerOrder> createState() => _SellerState();
}
class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixin {
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
// - Tab单独存储数据
final int pageSize = 10;
bool isLoadingMore = false;
bool isRefreshing = false;
// Tab单独存储状态
List<Map<String, dynamic>> _allOrders = [];
List<Map<String, dynamic>> _pendingPaymentOrders = [];
List<Map<String, dynamic>> _pendingVerificationOrders = [];
List<Map<String, dynamic>> _completedOrders = [];
List<Map<String, dynamic>> _closedOrders = [];
List<Map<String, dynamic>> _refundingOrders = [];
List<Map<String, dynamic>> _refundedOrders = [];
List<Map<String, dynamic>> _cancelledOrders = [];
// Tab的分页状态
Map<int, int> _pageNums = {};
Map<int, int> _totalCounts = {};
Map<int, bool> _hasMoreData = {};
Map<int, bool> _isLoading = {};
// 0->1->2->3->4->退5->退 6->
List tabList = [
{'id': '', 'name': "全部", 'index': 0},
{'id': 0, 'name': "待付款", 'index': 1},
{'id': 1, 'name': "待核销", 'index': 2},
{'id': 2, 'name': "已完成", 'index': 3},
{'id': 3, 'name': "已关闭", 'index': 4},
{'id': 4, 'name': "退款中", 'index': 5},
{'id': 5, 'name': "已退款", 'index': 6},
{'id': 6, 'name': "已取消", 'index': 7}
];
late ScrollController scrollController = ScrollController();
late TabController tabController = TabController(initialIndex: 0, length: tabList.length, vsync: this);
@override
void initState() {
super.initState();
//
for (var tab in tabList) {
_pageNums[tab['index']] = 1;
_totalCounts[tab['index']] = 0;
_hasMoreData[tab['index']] = true;
_isLoading[tab['index']] = false;
}
//
scrollController.addListener(_scrollListener);
// tab切换事件
tabController.addListener(_tabChanged);
//
getOrderList(false);
}
@override
void dispose() {
scrollController.removeListener(_scrollListener);
scrollController.dispose();
tabController.removeListener(_tabChanged);
tabController.dispose();
super.dispose();
}
// Tab的订单列表
List<Map<String, dynamic>> _getCurrentOrderList() {
final currentIndex = tabController.index;
switch (currentIndex) {
case 0: return _allOrders;
case 1: return _pendingPaymentOrders;
case 2: return _pendingVerificationOrders;
case 3: return _completedOrders;
case 4: return _closedOrders;
case 5: return _refundingOrders;
case 6: return _refundedOrders;
case 7: return _cancelledOrders;
default: return _allOrders;
}
}
// Tab的订单列表
void _setCurrentOrderList(List<Map<String, dynamic>> orders, bool loadMore) {
final currentIndex = tabController.index;
setState(() {
switch (currentIndex) {
case 0:
if (loadMore) {
_allOrders.addAll(orders);
} else {
_allOrders = orders;
}
break;
case 1:
if (loadMore) {
_pendingPaymentOrders.addAll(orders);
} else {
_pendingPaymentOrders = orders;
}
break;
case 2:
if (loadMore) {
_pendingVerificationOrders.addAll(orders);
} else {
_pendingVerificationOrders = orders;
}
break;
case 3:
if (loadMore) {
_completedOrders.addAll(orders);
} else {
_completedOrders = orders;
}
break;
case 4:
if (loadMore) {
_closedOrders.addAll(orders);
} else {
_closedOrders = orders;
}
break;
case 5:
if (loadMore) {
_refundingOrders.addAll(orders);
} else {
_refundingOrders = orders;
}
break;
case 6:
if (loadMore) {
_refundedOrders.addAll(orders);
} else {
_refundedOrders = orders;
}
break;
case 7:
if (loadMore) {
_cancelledOrders.addAll(orders);
} else {
_cancelledOrders = orders;
}
break;
}
});
}
//
void _scrollListener() {
final currentIndex = tabController.index;
if (scrollController.position.pixels == scrollController.position.maxScrollExtent &&
!_isLoading[currentIndex]! &&
_hasMoreData[currentIndex]!) {
getOrderList(true);
}
}
// Tab切换监听
void _tabChanged() {
if (tabController.index != tabController.previousIndex) {
// Tab没有数据
final currentOrders = _getCurrentOrderList();
if (currentOrders.isEmpty) {
getOrderList(false);
}
}
}
//
void getOrderList(bool loadMore) async {
final currentIndex = tabController.index;
final currentTab = tabList[currentIndex];
if (_isLoading[currentIndex]! || isRefreshing) return;
setState(() {
if (!loadMore) {
_pageNums[currentIndex] = 1;
isRefreshing = true;
} else {
_isLoading[currentIndex] = true;
}
});
try {
final status = currentTab['id'];
final res = await Http.post(ShopApi.sellerOrderList, data: {
'current': _pageNums[currentIndex], //
'size': pageSize,
'status': status == '' ? '' : status.toString(),
});
if (res['code'] == 200 && res['data'] != null) {
final data = res['data'];
final List<Map<String, dynamic>> newOrders = List<Map<String, dynamic>>.from(data['records'] ?? []);
final int total = data['total'] ?? 0;
debugPrint(data.toString(), wrapWidth: 1024);
// Tab的数据
_setCurrentOrderList(newOrders, loadMore);
// - 使total总数判断
setState(() {
//
_totalCounts[currentIndex] = total;
//
final currentOrderCount = _getCurrentOrderList().length;
_hasMoreData[currentIndex] = currentOrderCount < total;
//
if (_hasMoreData[currentIndex]!) {
_pageNums[currentIndex] = _pageNums[currentIndex]! + 1;
}
});
}
} catch (e) {
print('获取订单列表失败: $e');
setState(() {
_hasMoreData[currentIndex] = false;
});
} finally {
setState(() {
isRefreshing = false;
_isLoading[currentIndex] = false;
});
}
}
//
void _refreshData() {
final currentIndex = tabController.index;
setState(() {
_pageNums[currentIndex] = 1;
_totalCounts[currentIndex] = 0;
_isLoading[currentIndex] = false;
_hasMoreData[currentIndex] = true;
});
getOrderList(false);
}
//
String _getPaymentText(dynamic status) {
int statusCode = status is String ? int.tryParse(status) ?? 0 : (status is int ? status : 0);
switch (statusCode) {
case 0: //
return '待付款: ';
case 1: //
return '已付款: ';
case 2: //
return '实付款: ';
case 3: //
return '订单金额: ';
case 4: // 退
return '实付款: ';
case 5: // 退
return '退款金额: ';
case 6: //
return '订单金额: ';
default:
return '金额: ';
}
}
// Widget
Widget _buildOrderItem(Map<String, dynamic> order) {
return GestureDetector(
child: 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: [
Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 5.0,
children: [
ClipOval(
child: Image.asset(
'assets/images/avatar/img11.jpg',
width: 25.0,
),
),
Text(order['items'][0]['tenantName'] ?? '商家名称'),
Icon(
Icons.arrow_forward_ios_rounded,
color: Colors.grey,
size: 12.0,
),
],
),
Spacer(),
Text(
_getStatusText(order['status']),
style: TextStyle(color: _getStatusColor(order['status'])),
)
],
),
SizedBox(height: 10),
//
if (order['items'] != null && order['items'].isNotEmpty)
...order['items'].map<Widget>((item) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 80.0,
height: 80.0,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(4.0),
),
child: order['items'][0]['pic'] != null && order['items'][0]['pic'].isNotEmpty
? Image.network(
order['items'][0]['pic'],
width: 80.0,
height: 80.0,
fit: BoxFit.cover,
)
: Icon(
Icons.shopping_bag_outlined,
size: 40.0,
color: Colors.grey[400],
),
),
SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item['productName'] ?? '商品名称',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14),
),
SizedBox(height: 5),
Row(
children: [
Text(
'¥${item['salePrice'] ?? '0'}',
style: TextStyle(color: Colors.red, fontSize: 16),
),
Spacer(),
Text(
'x${item['quantity'] ?? '1'}',
style: TextStyle(color: Colors.grey),
),
],
),
],
),
)
],
)).toList(),
SizedBox(height: 10),
//
Container(
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(5.0),
),
child: Row(
children: [
Spacer(),
Text.rich(
TextSpan(children: [
TextSpan(text: _getPaymentText(order['status'])), //
TextSpan(
text: '¥${order['totalAmount'] ?? '0'}',
style: TextStyle(color: Colors.red, fontSize: 16),
),
]),
),
],
),
),
SizedBox(height: 10),
//
_buildActionButtons(order),
],
),
),
onTap: () {
Get.toNamed('/sellerOrder/detail', arguments: {'orderId': order['id']});
},
);
}
//
String _getStatusText(dynamic status) {
//
int statusCode = status is String ? int.tryParse(status) ?? 0 : (status is int ? status : 0);
switch (statusCode) {
case 0:
return '待付款';
case 1:
return '待核销';
case 2:
return '已完成';
case 3:
return '已关闭';
case 4:
return '退款中';
case 5:
return '已退款';
case 6:
return '已取消';
default:
return '未知状态';
}
}
//
Color _getStatusColor(dynamic status) {
int statusCode = status is String ? int.tryParse(status) ?? 0 : (status is int ? status : 0);
switch (statusCode) {
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 _buildActionButtons(dynamic orderObject) {
//
var status = orderObject['status'];
var orderId = orderObject['id'];
int statusCode = status is String ? int.tryParse(status) ?? 0 : (status is int ? status : 0);
switch (statusCode) {
case 0: //
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () => _cancelOrder(orderId),
style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.white)),
child: Text('取消订单'),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: () => _payOrder(orderId),
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Color(0xff07c160)),
foregroundColor: WidgetStateProperty.all(Colors.white),
),
child: Text('去支付'),
),
],
);
case 1: //
return Row(
mainAxisAlignment: MainAxisAlignment.end,
);
case 2: //
return Row(
mainAxisAlignment: MainAxisAlignment.end,
);
default:
return SizedBox.shrink();
}
}
//
void _cancelOrder(String orderId) async {
try {
final res = await Http.post('${ShopApi.cancelGoodsOrder}/$orderId');
print('取消订单成功-------------->${res}');
if (res['code'] == 200) {
MyToast().tip(
title: '订单已取消',
position: 'center',
type: 'success',
);
// tab
_refreshAllRelatedTabs();
}
} catch (e) {
print('取消订单失败-------------->${e}');
}
}
// tab
void _refreshAllRelatedTabs() {
// tab索引(0)(1)(7)
List<int> tabsToRefresh = [0, 1, 7];
for (int tabIndex in tabsToRefresh) {
_resetTabData(tabIndex);
// tab
if (tabController.index == tabIndex) {
getOrderList(false);
}
}
}
void _payOrder(_orderId) {
// pages/index/index
Wxsdk.openMiniApp(orderId: _orderId);
}
// tab的数据
void _resetTabData(int index) {
setState(() {
_pageNums[index] = 1;
_totalCounts[index] = 0;
_isLoading[index] = false;
_hasMoreData[index] = true;
switch (index) {
case 0: _allOrders = []; break;
case 1: _pendingPaymentOrders = []; break;
case 2: _pendingVerificationOrders = []; break;
case 3: _completedOrders = []; break;
case 4: _closedOrders = []; break;
case 5: _refundingOrders = []; break;
case 6: _refundedOrders = []; break;
case 7: _cancelledOrders = []; break;
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
backgroundColor: Colors.white,
appBar: AppBar(
titleSpacing: 1.0,
title: Text(
'商家订单',
style: TextStyle(fontSize: 18),
),
bottom: PreferredSize(
preferredSize: Size.fromHeight(45.0),
child: Container(
height: 45.0,
alignment: Alignment.centerLeft,
child: TabBar(
controller: tabController,
tabAlignment: TabAlignment.start,
isScrollable: true,
padding: EdgeInsets.only(left: 0),
indicatorPadding: EdgeInsets.zero,
labelPadding: EdgeInsets.symmetric(horizontal: 10.0),
tabs: tabList
.map((item) => Container(
constraints: BoxConstraints(minWidth: 70),
alignment: Alignment.center,
child: Badge.count(
backgroundColor: Colors.red,
offset: Offset(14, -4),
count: item['badge'] ?? 0,
isLabelVisible: item['badge'] != null && item['badge'] > 0,
child: Text(
item['name'],
style: TextStyle(fontSize: 16),
overflow: TextOverflow.ellipsis,
),
),
))
.toList(),
overlayColor: WidgetStateProperty.all(Colors.transparent),
unselectedLabelColor: Colors.black87,
labelColor: Color(0xFFFF5000),
indicator: UnderlineTabIndicator(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(color: Color(0xFFFF5000), width: 2.0),
),
indicatorSize: TabBarIndicatorSize.label,
unselectedLabelStyle: TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
labelStyle: TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.w700),
dividerHeight: 0,
),
),
),
),
body: TabBarView(
controller: tabController,
children: List.generate(tabList.length, (index) {
return RefreshIndicator(
onRefresh: () async {
_refreshData();
},
child: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: Container(
color: Colors.grey[50],
child: Builder(
builder: (context) {
final currentOrders = _getOrderListByIndex(index);
final isLoading = _isLoading[index] ?? false;
final hasMoreData = _hasMoreData[index] ?? false;
return currentOrders.isEmpty && !isRefreshing
? emptyTip()
: ListView.builder(
controller: scrollController,
physics: AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.all(10.0),
itemCount: currentOrders.length + (hasMoreData ? 1 : 0),
itemBuilder: (context, itemIndex) {
if (itemIndex == currentOrders.length) {
return _buildLoadMoreIndicator(isLoading);
}
return _buildOrderItem(currentOrders[itemIndex]);
},
);
}
),
),
),
);
}),
),
);
}
//
List<Map<String, dynamic>> _getOrderListByIndex(int index) {
switch (index) {
case 0: return _allOrders;
case 1: return _pendingPaymentOrders;
case 2: return _pendingVerificationOrders;
case 3: return _completedOrders;
case 4: return _closedOrders;
case 5: return _refundingOrders;
case 6: return _refundedOrders;
case 7: return _cancelledOrders;
default: return _allOrders;
}
}
//
Widget _buildLoadMoreIndicator(bool isLoading) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 15.0),
child: Center(
child: isLoading
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
),
SizedBox(width: 10),
Text('加载中...', style: TextStyle(color: Colors.grey)),
],
)
: Text('没有更多数据了', style: TextStyle(color: Colors.grey)),
),
);
}
Widget emptyTip() {
return Container(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/empty.png',
width: 100.0,
),
SizedBox(height: 10),
Text(
'还没有相关订单~',
style: TextStyle(color: Colors.grey, fontSize: 12.0),
)
],
),
);
}
}

View File

@ -22,8 +22,8 @@ import 'package:loopin/pages/my/user_info.dart';
import 'package:loopin/pages/my/vloger.dart'; import 'package:loopin/pages/my/vloger.dart';
import 'package:loopin/pages/my/all_function.dart'; import 'package:loopin/pages/my/all_function.dart';
import 'package:loopin/pages/my/merchant/income.dart'; import 'package:loopin/pages/my/merchant/income.dart';
import 'package:loopin/pages/my/merchant/order.dart'; import 'package:loopin/pages/order/my_order.dart';
import 'package:loopin/pages/order/seller.dart'; import 'package:loopin/pages/order/seller_order.dart';
import 'package:loopin/pages/search/index.dart'; import 'package:loopin/pages/search/index.dart';
import 'package:loopin/pages/search/search-result.dart'; import 'package:loopin/pages/search/search-result.dart';
import 'package:loopin/pages/video/commonVideo.dart'; import 'package:loopin/pages/video/commonVideo.dart';
@ -35,6 +35,7 @@ import '../pages/auth/login.dart';
// //
import '../pages/goods/detail.dart'; import '../pages/goods/detail.dart';
import '../pages/order/detail.dart'; import '../pages/order/detail.dart';
import '../pages/order/seller_detail.dart';
// //
import '../pages/order/index.dart'; import '../pages/order/index.dart';
// //
@ -47,7 +48,9 @@ final Map<String, Widget> routes = {
// '/chatNoFriend': const ChatNoFriend(), // '/chatNoFriend': const ChatNoFriend(),
// '/chatGroup': const ChatGroup(), // '/chatGroup': const ChatGroup(),
'/order': const Order(), '/order': const Order(),
'/sellerOrder': const Seller(), '/sellerOrder': const SellerOrder(),
'/sellerOrder/detail': const SellerOrderDetail(),
'/myOrder': const MyOrder(),
'/order/detail': const OrderDetail(), '/order/detail': const OrderDetail(),
'/vloger': const Vloger(), '/vloger': const Vloger(),
'/report': const ReportPage(), '/report': const ReportPage(),
@ -58,7 +61,6 @@ final Map<String, Widget> routes = {
'/setting': const Setting(), '/setting': const Setting(),
'/functions': const AllFunctionsPage(), '/functions': const AllFunctionsPage(),
'/merchant/income': const SellerRevenue(), '/merchant/income': const SellerRevenue(),
//'/merchant/order': const SellerRevenue(),
'/userInfo': const UserInfo(), '/userInfo': const UserInfo(),
'/notifications': const Setting(), '/notifications': const Setting(),
'/privacy': const Setting(), '/privacy': const Setting(),