503 lines
15 KiB
Dart
503 lines
15 KiB
Dart
import 'package:flutter/material.dart';
|
||
import '../../components/appbar.dart';
|
||
import '../../apis/app.dart';
|
||
import '../../components/emptyState.dart';
|
||
|
||
class GradientUnderlineTabIndicator extends Decoration {
|
||
final double borderSide;
|
||
final EdgeInsetsGeometry insets;
|
||
final List<Color> gradientColors;
|
||
|
||
const GradientUnderlineTabIndicator({
|
||
this.borderSide = 3.0,
|
||
this.insets = const EdgeInsets.symmetric(horizontal: 10.0),
|
||
this.gradientColors = const [Color(0xFF80DAA4), Color(0xFF79DDED)],
|
||
});
|
||
|
||
@override
|
||
BoxPainter createBoxPainter([VoidCallback? onChanged]) {
|
||
return _GradientUnderlinePainter(
|
||
borderSide: borderSide,
|
||
insets: insets,
|
||
gradientColors: gradientColors,
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
class _GradientUnderlinePainter extends BoxPainter {
|
||
final double borderSide;
|
||
final EdgeInsetsGeometry insets;
|
||
final List<Color> gradientColors;
|
||
|
||
_GradientUnderlinePainter({
|
||
required this.borderSide,
|
||
required this.insets,
|
||
required this.gradientColors,
|
||
});
|
||
|
||
@override
|
||
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
|
||
final Rect rect = offset & configuration.size!;
|
||
final TextDirection textDirection = configuration.textDirection!;
|
||
final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
|
||
final Paint paint = Paint()
|
||
..shader = LinearGradient(
|
||
colors: gradientColors,
|
||
begin: Alignment.centerLeft,
|
||
end: Alignment.centerRight,
|
||
).createShader(Rect.fromLTWH(
|
||
indicator.left,
|
||
indicator.bottom - borderSide,
|
||
indicator.width,
|
||
borderSide,
|
||
))
|
||
..style = PaintingStyle.fill;
|
||
|
||
canvas.drawRect(
|
||
Rect.fromLTWH(
|
||
indicator.left,
|
||
indicator.bottom - borderSide,
|
||
indicator.width,
|
||
borderSide,
|
||
),
|
||
paint,
|
||
);
|
||
}
|
||
}
|
||
|
||
// 订单信息模型
|
||
class OrderInfo {
|
||
final int id;
|
||
final String orderInfoId;
|
||
final int orderId;
|
||
final String workName;
|
||
final String workStatus;
|
||
final String city;
|
||
final String scenic;
|
||
final String source;
|
||
final String status;
|
||
final String workAddress; // 添加workAddress字段
|
||
|
||
|
||
OrderInfo({
|
||
required this.id,
|
||
required this.orderInfoId,
|
||
required this.orderId,
|
||
required this.workName,
|
||
required this.workStatus,
|
||
required this.city,
|
||
required this.scenic,
|
||
required this.source,
|
||
required this.status,
|
||
required this.workAddress, // 添加到构造函数
|
||
|
||
});
|
||
|
||
factory OrderInfo.fromJson(Map<String, dynamic> json) {
|
||
return OrderInfo(
|
||
id: json['id'] ?? 0,
|
||
orderInfoId: json['orderInfoId'] ?? '',
|
||
orderId: json['orderId'] ?? 0,
|
||
workName: json['workName'] ?? '',
|
||
workStatus: json['workStatus']?.toString() ?? '',
|
||
city: json['city'] ?? '',
|
||
scenic: json['scenic'] ?? '',
|
||
source: json['source'] ?? '',
|
||
status: json['status']?.toString() ?? '',
|
||
workAddress: json['workAddress'] ?? '', // 从JSON中解析workAddress
|
||
|
||
);
|
||
}
|
||
}
|
||
|
||
// 订单模型
|
||
class Order {
|
||
final int orderId;
|
||
final String createTime;
|
||
final String startTime;
|
||
final String endTime;
|
||
final double money;
|
||
final String status;
|
||
final List<OrderInfo> orderInfoList;
|
||
final String? remainTimeString; // 添加剩余时间字段
|
||
|
||
Order({
|
||
required this.orderId,
|
||
required this.createTime,
|
||
required this.startTime,
|
||
required this.endTime,
|
||
required this.money,
|
||
required this.status,
|
||
required this.orderInfoList,
|
||
this.remainTimeString, // 可空字段
|
||
});
|
||
|
||
factory Order.fromJson(Map<String, dynamic> json) {
|
||
return Order(
|
||
orderId: json['orderId'] ?? 0,
|
||
createTime: json['createTime'] ?? '',
|
||
startTime: json['startTime'] ?? '',
|
||
endTime: json['endTime'] ?? '',
|
||
money: (json['money'] ?? 0).toDouble(),
|
||
status: json['status']?.toString() ?? '',
|
||
orderInfoList: (json['orderInfoList'] as List?)
|
||
?.map((e) => OrderInfo.fromJson(e))
|
||
.toList() ?? [],
|
||
remainTimeString: json['remainTimeString'], // 从JSON中解析剩余时间
|
||
);
|
||
}
|
||
}
|
||
|
||
class HistoryPage extends StatefulWidget {
|
||
const HistoryPage({super.key});
|
||
|
||
@override
|
||
State<HistoryPage> createState() => _HistoryPageState();
|
||
}
|
||
|
||
class _HistoryPageState extends State<HistoryPage>
|
||
with SingleTickerProviderStateMixin {
|
||
late TabController _tabController;
|
||
List<Order> _data = [];
|
||
bool _isLoading = false;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_tabController = TabController(length: 4, vsync: this);
|
||
_loadOrders();
|
||
}
|
||
|
||
Future<void> _loadOrders() async {
|
||
setState(() {
|
||
_isLoading = true;
|
||
});
|
||
|
||
try {
|
||
final result = await userApi.getOrder();
|
||
print("历史订单列表result:$result");
|
||
if (result['code'] == 200) {
|
||
setState(() {
|
||
_data = (result['rows'] as List)
|
||
.map((item) => Order.fromJson(item))
|
||
.toList();
|
||
|
||
});
|
||
} else {
|
||
// 处理错误情况
|
||
debugPrint("获取订单失败: ${result['msg']}");
|
||
}
|
||
} catch (e) {
|
||
debugPrint("获取订单异常: $e");
|
||
} finally {
|
||
setState(() {
|
||
_isLoading = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_tabController.dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
resizeToAvoidBottomInset: false,
|
||
appBar: const CustomAppBar(title: '历史订单'),
|
||
body: Container(
|
||
decoration: const BoxDecoration(color: Color(0xff0C0C16)),
|
||
child: Column(
|
||
children: [
|
||
_buildTabBar(),
|
||
Expanded(
|
||
child: TabBarView(
|
||
controller: _tabController,
|
||
children: [
|
||
_buildOrderList(null),
|
||
_buildOrderList('3'), // 展示中
|
||
_buildOrderList('0'), // 未展示
|
||
_buildOrderList('2'), // 已停止
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildTabBar() {
|
||
return Container(
|
||
color: const Color(0xFF1F212E),
|
||
child: TabBar(
|
||
controller: _tabController,
|
||
tabs: const [
|
||
Tab(text: '全部'),
|
||
Tab(text: '展示中'),
|
||
Tab(text: '未展示'),
|
||
Tab(text: '已停止'),
|
||
],
|
||
indicator: const GradientUnderlineTabIndicator(
|
||
borderSide: 3.0,
|
||
insets: EdgeInsets.symmetric(horizontal: 10.0),
|
||
gradientColors: [Color(0xFF80DAA4), Color(0xFF79DDED)],
|
||
),
|
||
labelColor: Colors.white,
|
||
unselectedLabelColor: Colors.grey,
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildOrderList(String? status) {
|
||
if (_isLoading) {
|
||
return const Center(child: CircularProgressIndicator());
|
||
}
|
||
|
||
final filteredOrders = status == null
|
||
? _data
|
||
: _data.where((order) => order.status == status).toList();
|
||
|
||
if (filteredOrders.isEmpty) {
|
||
return const EmptyState(message: '暂无订单数据');
|
||
}
|
||
|
||
return RefreshIndicator(
|
||
onRefresh: _loadOrders,
|
||
child: ListView.builder(
|
||
itemCount: filteredOrders.length,
|
||
itemBuilder: (context, index) {
|
||
final order = filteredOrders[index];
|
||
return _buildOrderCard(order);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildOrderCard(Order order) {
|
||
if (order.orderInfoList.isEmpty) return const SizedBox();
|
||
|
||
return GestureDetector(
|
||
onTap: () {
|
||
// 添加详细的日志打印
|
||
print('准备传递的订单数据:');
|
||
print('orderId: ${order.orderId}');
|
||
print("订单状态(status): ${order.status} (${_getStatusText(order.status)})");
|
||
print('orderInfoId: ${order.orderInfoList[0].orderInfoId}');
|
||
print('createTime: ${order.createTime}');
|
||
print('money: ${order.money}');
|
||
print('order: ${order}');
|
||
print('orderInfoList长度: ${order.orderInfoList.length}');
|
||
print('order.remainTimeString: ${order.remainTimeString}');
|
||
|
||
|
||
Navigator.pushNamed(context, '/historyDetail', arguments: order);
|
||
},
|
||
child: Container(
|
||
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||
padding: const EdgeInsets.all(12),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFF1F212E),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// 订单头部信息(只显示一次)
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
_formatDateTime(order.createTime),
|
||
style: const TextStyle(
|
||
fontSize: 12,
|
||
color: Color(0xffC1C1C1),
|
||
),
|
||
),
|
||
Text(
|
||
_getStatusText(order.status),
|
||
style: TextStyle(
|
||
fontSize: 12,
|
||
color: _getStatusColor(order.status),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 12),
|
||
// 循环展示 orderInfoList
|
||
ListView.separated(
|
||
shrinkWrap: true, // 重要:使ListView适应内容高度
|
||
physics: const NeverScrollableScrollPhysics(), // 禁用滚动
|
||
itemCount: order.orderInfoList.length,
|
||
separatorBuilder: (context, index) => const Padding(
|
||
padding: EdgeInsets.symmetric(vertical: 8),
|
||
child: Divider(
|
||
color: Color(0xFF2A2C37),
|
||
height: 1,
|
||
),
|
||
),
|
||
itemBuilder: (context, index) {
|
||
final orderInfo = order.orderInfoList[index];
|
||
return Row(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// orderInfo.workAddress
|
||
ClipRRect(
|
||
borderRadius: BorderRadius.circular(4),
|
||
child: Image.network(
|
||
orderInfo.workAddress, // 使用workAddress作为图片URL
|
||
width: 60,
|
||
height: 83,
|
||
fit: BoxFit.cover,
|
||
errorBuilder: (context, error, stackTrace) {
|
||
return Container(
|
||
width: 60,
|
||
height: 83,
|
||
color: const Color(0xFF1F212E),
|
||
child: const Center(
|
||
child: Icon(
|
||
Icons.image_not_supported,
|
||
color: Colors.grey,
|
||
size: 24,
|
||
),
|
||
),
|
||
);
|
||
},
|
||
loadingBuilder: (context, child, loadingProgress) {
|
||
if (loadingProgress == null) return child;
|
||
return Container(
|
||
width: 60,
|
||
height: 83,
|
||
color: const Color(0xFF1F212E),
|
||
child: const Center(
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 2,
|
||
),
|
||
),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
const SizedBox(width: 8),
|
||
Expanded(
|
||
child: SizedBox(
|
||
height: 83,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
orderInfo.workName,
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
Text(
|
||
_getWorkStatusText(orderInfo.workStatus),
|
||
style: TextStyle(
|
||
fontSize: 12,
|
||
color: _getWorkStatusColor(orderInfo.workStatus),
|
||
),
|
||
),
|
||
Text(
|
||
"展示时间:${_formatDateTime(order.startTime)}",
|
||
style: const TextStyle(
|
||
fontSize: 12,
|
||
color: Color(0xffC1C1C1),
|
||
),
|
||
),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
"展示位置:${orderInfo.scenic}",
|
||
style: const TextStyle(
|
||
fontSize: 12,
|
||
color: Color(0xffC1C1C1),
|
||
),
|
||
),
|
||
// 只在最后一个项目显示总金额
|
||
if (index == order.orderInfoList.length - 1)
|
||
Text(
|
||
"¥${order.money / 1000}",
|
||
style: const TextStyle(
|
||
fontSize: 16,
|
||
color: Color(0xffF42845),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
String _formatDateTime(String dateTimeStr) {
|
||
try {
|
||
final dateTime = DateTime.parse(dateTimeStr);
|
||
return "${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} 12:00:00";
|
||
} catch (e) {
|
||
return "$dateTimeStr 12:00:00";
|
||
}
|
||
}
|
||
|
||
String _getStatusText(String status) {
|
||
switch (status) {
|
||
case '0':
|
||
return '未展示';
|
||
case '2':
|
||
return '已停止';
|
||
case '3':
|
||
return '展示中';
|
||
default:
|
||
return '';
|
||
}
|
||
}
|
||
|
||
Color _getStatusColor(String status) {
|
||
switch (status) {
|
||
case '3':
|
||
return const Color(0xff78E7A4);
|
||
case '2':
|
||
return const Color(0xffDE8D3D);
|
||
case '0':
|
||
return const Color(0xff79B7FF);
|
||
default:
|
||
return Colors.grey;
|
||
}
|
||
}
|
||
|
||
String _getWorkStatusText(String workStatus) {
|
||
switch (workStatus) {
|
||
case '3':
|
||
return '审核成功';
|
||
case '4':
|
||
return '审核失败';
|
||
default:
|
||
return '审核中';
|
||
}
|
||
}
|
||
|
||
Color _getWorkStatusColor(String workStatus) {
|
||
switch (workStatus) {
|
||
case '3': // 审核成功
|
||
return const Color(0xff78E7A4);
|
||
case '4': // 审核失败
|
||
return const Color(0xffF42845);
|
||
default: // 审核中
|
||
return const Color(0xff79B7FF);
|
||
}
|
||
}
|
||
} |