465 lines
14 KiB
Dart
465 lines
14 KiB
Dart
|
import 'package:flutter/material.dart';
|
||
|
import 'package:loopin/api/shop_api.dart';
|
||
|
import 'package:loopin/service/http.dart';
|
||
|
import '../../../behavior/custom_scroll_behavior.dart';
|
||
|
|
||
|
class SellerRevenue extends StatefulWidget {
|
||
|
const SellerRevenue({super.key});
|
||
|
|
||
|
@override
|
||
|
State<SellerRevenue> createState() => _SellerRevenueState();
|
||
|
}
|
||
|
|
||
|
class _SellerRevenueState extends State<SellerRevenue> {
|
||
|
// 分页内容
|
||
|
int pageNum = 1;
|
||
|
final int pageSize = 10;
|
||
|
bool isLoadingMore = false;
|
||
|
bool isRefreshing = false;
|
||
|
List<Map<String, dynamic>> revenueList = [];
|
||
|
int totalRecords = 0; // 添加总记录数
|
||
|
|
||
|
// 营收统计数据
|
||
|
double totalRevenue = 1000.00;
|
||
|
double pendingCount = 17;
|
||
|
double settledCount = 30;
|
||
|
|
||
|
late ScrollController scrollController = ScrollController();
|
||
|
|
||
|
@override
|
||
|
void initState() {
|
||
|
super.initState();
|
||
|
// 监听滚动事件
|
||
|
scrollController.addListener(_scrollListener);
|
||
|
// 获取营收摘要
|
||
|
getRevenueInfo();
|
||
|
// 获取初始流水列表
|
||
|
getRevenueList(false);
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
void dispose() {
|
||
|
scrollController.removeListener(_scrollListener);
|
||
|
scrollController.dispose();
|
||
|
super.dispose();
|
||
|
}
|
||
|
|
||
|
// 滚动监听
|
||
|
void _scrollListener() {
|
||
|
if (scrollController.position.pixels >=
|
||
|
scrollController.position.maxScrollExtent - 50 && // 添加一个阈值,提前加载
|
||
|
!isLoadingMore &&
|
||
|
!isRefreshing &&
|
||
|
revenueList.isNotEmpty &&
|
||
|
revenueList.length < totalRecords) { // 确保有数据且未加载完所有数据时才加载更多
|
||
|
|
||
|
getRevenueList(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 获取营收摘要
|
||
|
void getRevenueInfo() async {
|
||
|
try {
|
||
|
final res = await Http.post(ShopApi.revenueList, data: {});
|
||
|
|
||
|
if (res['code'] == 200 && res['data'] != null) {
|
||
|
final data = res['data'];
|
||
|
totalRevenue = data['moneyIn'] ?? 0.00;
|
||
|
pendingCount = data['credited']?? 0.00;
|
||
|
settledCount = data['pending'] ?? 0.00;
|
||
|
}
|
||
|
} catch (e) {
|
||
|
print('获取营收摘要: $e');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 获取流水列表
|
||
|
void getRevenueList(bool loadMore) async {
|
||
|
if (isLoadingMore || isRefreshing) return;
|
||
|
|
||
|
setState(() {
|
||
|
if (!loadMore) {
|
||
|
pageNum = 1; // 重置为第一页
|
||
|
isRefreshing = true;
|
||
|
revenueList = [];
|
||
|
totalRecords = 0; // 重置总记录数
|
||
|
} else {
|
||
|
isLoadingMore = true;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
try {
|
||
|
final res = await Http.post(ShopApi.revenueList, data: {
|
||
|
'current': pageNum, // 当前页码
|
||
|
'size': pageSize,
|
||
|
});
|
||
|
|
||
|
if (res['code'] == 200 && res['data'] != null) {
|
||
|
final data = res['data'];
|
||
|
final List<Map<String, dynamic>> newRevenues = List<Map<String, dynamic>>.from(data['records'] ?? []);
|
||
|
final int total = data['total'];
|
||
|
debugPrint(data.toString(), wrapWidth: 1024);
|
||
|
setState(() {
|
||
|
if (loadMore) {
|
||
|
revenueList.addAll(newRevenues);
|
||
|
} else {
|
||
|
revenueList = newRevenues;
|
||
|
}
|
||
|
|
||
|
totalRecords = total;
|
||
|
if (newRevenues.isNotEmpty && revenueList.length < total) {
|
||
|
pageNum++; // 增加页码
|
||
|
isLoadingMore = false; // 重置加载状态,允许再次加载
|
||
|
} else {
|
||
|
isLoadingMore = false; // 没有更多数据
|
||
|
}
|
||
|
|
||
|
isRefreshing = false;
|
||
|
});
|
||
|
}
|
||
|
} catch (e) {
|
||
|
print('获取营收列表失败: $e');
|
||
|
setState(() {
|
||
|
isLoadingMore = false;
|
||
|
isRefreshing = false;
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 刷新数据时重置分页状态
|
||
|
void _refreshData() {
|
||
|
setState(() {
|
||
|
pageNum = 1;
|
||
|
isLoadingMore = false;
|
||
|
totalRecords = 0;
|
||
|
});
|
||
|
getRevenueList(false);
|
||
|
}
|
||
|
|
||
|
// 构建营收项
|
||
|
Widget _buildRevenueItem(Map<String, dynamic> revenue) {
|
||
|
return Container(
|
||
|
margin: EdgeInsets.only(bottom: 10.0),
|
||
|
child: Row(
|
||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
|
children: [
|
||
|
// 第一列:用户
|
||
|
Expanded(
|
||
|
flex: 1,
|
||
|
child: Text(
|
||
|
revenue['changeDesc']??'未知',
|
||
|
style: TextStyle(
|
||
|
fontSize: 14,
|
||
|
fontWeight: FontWeight.w500,
|
||
|
color: Colors.black,
|
||
|
),
|
||
|
overflow: TextOverflow.ellipsis,
|
||
|
),
|
||
|
),
|
||
|
// 第二列:流水明细
|
||
|
Expanded(
|
||
|
flex: 1,
|
||
|
child: Text(
|
||
|
'${revenue['changeType'] == '1' ? '+' : '-'}${revenue['changeAmount']}',
|
||
|
style: TextStyle(
|
||
|
fontSize: 14,
|
||
|
color: revenue['changeType'] == '1' ? Colors.red : Colors.green,
|
||
|
),
|
||
|
overflow: TextOverflow.ellipsis,
|
||
|
textAlign: TextAlign.center,
|
||
|
),
|
||
|
),
|
||
|
// 第三列:流水日期
|
||
|
Expanded(
|
||
|
flex: 1,
|
||
|
child: Text(
|
||
|
revenue['createTime'] ?? '日期',
|
||
|
style: TextStyle(
|
||
|
fontSize: 12,
|
||
|
color: Colors.black,
|
||
|
),
|
||
|
textAlign: TextAlign.right,
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// 构建顶部营收面板
|
||
|
Widget _buildRevenuePanel() {
|
||
|
return Container(
|
||
|
margin: EdgeInsets.symmetric(horizontal: 12.0),
|
||
|
padding: EdgeInsets.all(20),
|
||
|
decoration: BoxDecoration(
|
||
|
color: Color(0xFF4477FC),
|
||
|
borderRadius: BorderRadius.circular(6),
|
||
|
boxShadow: [
|
||
|
BoxShadow(
|
||
|
color: Colors.black.withAlpha(15),
|
||
|
blurRadius: 10,
|
||
|
offset: Offset(0, 2),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
child: Column(
|
||
|
children: [
|
||
|
// 总入账
|
||
|
Text(
|
||
|
'总入账',
|
||
|
style: TextStyle(
|
||
|
fontSize: 14,
|
||
|
color: Colors.white,
|
||
|
),
|
||
|
),
|
||
|
SizedBox(height: 8),
|
||
|
Text(
|
||
|
'¥$totalRevenue',
|
||
|
style: TextStyle(
|
||
|
fontSize: 28,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
color: Colors.white,
|
||
|
),
|
||
|
),
|
||
|
SizedBox(height: 20),
|
||
|
// 待入账和已入账
|
||
|
Row(
|
||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||
|
children: [
|
||
|
Column(
|
||
|
children: [
|
||
|
Text(
|
||
|
'待入账',
|
||
|
style: TextStyle(
|
||
|
fontSize: 14,
|
||
|
color: Colors.white,
|
||
|
),
|
||
|
),
|
||
|
SizedBox(height: 4),
|
||
|
Text(
|
||
|
'$pendingCount',
|
||
|
style: TextStyle(
|
||
|
fontSize: 18,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
color: Colors.white,
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
Container(
|
||
|
width: 1,
|
||
|
height: 30,
|
||
|
color: Colors.white,
|
||
|
),
|
||
|
Column(
|
||
|
children: [
|
||
|
Text(
|
||
|
'已入账',
|
||
|
style: TextStyle(
|
||
|
fontSize: 14,
|
||
|
color: Colors.white,
|
||
|
),
|
||
|
),
|
||
|
SizedBox(height: 4),
|
||
|
Text(
|
||
|
'$settledCount',
|
||
|
style: TextStyle(
|
||
|
fontSize: 18,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
color: Colors.white,
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
backgroundColor:Color(0xFFF8F8F8),
|
||
|
body: Stack(
|
||
|
children: [
|
||
|
// 背景图片 - 从顶部开始
|
||
|
Positioned.fill(
|
||
|
child: Image.asset(
|
||
|
'assets/images/income_bg.png',
|
||
|
fit: BoxFit.contain,
|
||
|
alignment: Alignment.topCenter,
|
||
|
),
|
||
|
),
|
||
|
// 主要内容区域
|
||
|
Column(
|
||
|
children: [
|
||
|
// AppBar区域
|
||
|
Container(
|
||
|
height: kToolbarHeight + MediaQuery.of(context).padding.top,
|
||
|
child: AppBar(
|
||
|
title: Text(
|
||
|
'商家营收',
|
||
|
style: TextStyle(
|
||
|
fontSize: 18,
|
||
|
color: Colors.white,
|
||
|
),
|
||
|
),
|
||
|
centerTitle: true,
|
||
|
backgroundColor: Colors.transparent,
|
||
|
elevation: 0,
|
||
|
iconTheme: IconThemeData(color: Colors.white),
|
||
|
),
|
||
|
),
|
||
|
// 营收面板
|
||
|
SizedBox(height: 10),
|
||
|
_buildRevenuePanel(),
|
||
|
SizedBox(height: 10),
|
||
|
// 流水明细
|
||
|
Expanded(
|
||
|
child: Container(
|
||
|
color: Colors.white,
|
||
|
margin: EdgeInsets.symmetric(horizontal: 12.0),
|
||
|
child: Column(
|
||
|
children: [
|
||
|
// 流水明细标题
|
||
|
Container(
|
||
|
padding: EdgeInsets.only(
|
||
|
left: 12.0,
|
||
|
right: 12.0,
|
||
|
top: 10.0,
|
||
|
),
|
||
|
child: Row(
|
||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
|
children: [
|
||
|
Expanded(
|
||
|
flex: 1,
|
||
|
child: Text(
|
||
|
'来源',
|
||
|
style: TextStyle(
|
||
|
fontSize: 16,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
color: Colors.black,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
Expanded(
|
||
|
flex: 1,
|
||
|
child: Text(
|
||
|
'流水明细',
|
||
|
style: TextStyle(
|
||
|
fontSize: 16,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
color: Colors.black,
|
||
|
),
|
||
|
textAlign: TextAlign.center,
|
||
|
),
|
||
|
),
|
||
|
Expanded(
|
||
|
flex: 1,
|
||
|
child: Text(
|
||
|
'流水日期',
|
||
|
style: TextStyle(
|
||
|
fontSize: 16,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
color: Colors.black,
|
||
|
),
|
||
|
textAlign: TextAlign.right,
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
SizedBox(height: 10),
|
||
|
// 流水列表区域
|
||
|
Expanded(
|
||
|
child: Container(
|
||
|
margin: EdgeInsets.symmetric(horizontal: 12.0),
|
||
|
child: RefreshIndicator(
|
||
|
onRefresh: () async {
|
||
|
_refreshData();
|
||
|
},
|
||
|
child: ScrollConfiguration(
|
||
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||
|
child: revenueList.isEmpty && !isRefreshing
|
||
|
? emptyTip()
|
||
|
: Container(
|
||
|
child: ListView.builder(
|
||
|
padding: EdgeInsets.zero,
|
||
|
controller: scrollController,
|
||
|
physics: AlwaysScrollableScrollPhysics(),
|
||
|
itemCount: revenueList.length + (isLoadingMore ? 1 : 0),
|
||
|
itemBuilder: (context, index) {
|
||
|
if (index == revenueList.length) {
|
||
|
return _buildLoadMoreIndicator();
|
||
|
}
|
||
|
return _buildRevenueItem(revenueList[index]);
|
||
|
},
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// 加载更多指示器
|
||
|
Widget _buildLoadMoreIndicator() {
|
||
|
return Padding(
|
||
|
padding: EdgeInsets.symmetric(vertical: 15.0),
|
||
|
child: Center(
|
||
|
child: isLoadingMore
|
||
|
? Row(
|
||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
children: [
|
||
|
SizedBox(
|
||
|
width: 20,
|
||
|
height: 20,
|
||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||
|
),
|
||
|
SizedBox(width: 10),
|
||
|
Text('加载中...', style: TextStyle(color: Colors.grey)),
|
||
|
],
|
||
|
)
|
||
|
: revenueList.length >= totalRecords
|
||
|
? Text('没有更多数据了', style: TextStyle(color: Colors.grey))
|
||
|
: SizedBox(), // 如果还有数据但不在加载中,不显示任何内容
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Widget emptyTip() {
|
||
|
return Container(
|
||
|
width: double.infinity,
|
||
|
padding: EdgeInsets.all(12.0),
|
||
|
color: Colors.white,
|
||
|
child: Column(
|
||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
children: [
|
||
|
Image.asset(
|
||
|
'assets/images/empty.png',
|
||
|
width: 100.0,
|
||
|
errorBuilder: (context, error, stackTrace) {
|
||
|
return Icon(Icons.receipt_long, size: 60, color: Colors.grey);
|
||
|
},
|
||
|
),
|
||
|
SizedBox(height: 10),
|
||
|
Text(
|
||
|
'还没有营收记录~',
|
||
|
style: TextStyle(color: Colors.grey, fontSize: 12.0),
|
||
|
)
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|