flutter/lib/pages/order/seller_order.dart
2025-09-12 17:23:08 +08:00

728 lines
23 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: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),
)
],
),
);
}
}