Files
zhinian_manage/lib/pages/order_detail_page.dart

347 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../providers/order_provider.dart';
import '../theme.dart';
import '../l10n/app_localizations.dart';
import '../widgets/skeleton.dart';
class OrderDetailPage extends ConsumerStatefulWidget {
final String orderId;
const OrderDetailPage({super.key, required this.orderId});
@override
ConsumerState<OrderDetailPage> createState() => _OrderDetailPageState();
}
class _OrderDetailPageState extends ConsumerState<OrderDetailPage> {
@override
void initState() {
super.initState();
Future.microtask(() {
ref.read(orderProvider.notifier).getDetail(widget.orderId);
});
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final state = ref.watch(orderProvider);
final order = state.currentOrder;
if (order == null) {
return Scaffold(
backgroundColor: AppColors.background,
appBar: AppBar(title: Text(l10n.orderDetail)),
body: const DetailPageSkeleton(),
);
}
return Scaffold(
backgroundColor: AppColors.background,
appBar: AppBar(title: Text(l10n.orderDetail)),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildStatusHeader(order),
const SizedBox(height: 20),
_buildProductCard(order, l10n),
const SizedBox(height: 20),
_buildCustomerCard(order, l10n),
const SizedBox(height: 20),
_buildPaymentCard(order, l10n),
const SizedBox(height: 20),
_buildInfoCard(order, l10n),
],
),
),
);
}
Widget _buildStatusHeader(order) {
return Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
gradient: AppGradients.primary,
borderRadius: BorderRadius.circular(20),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Text(
order.statusText,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
const Spacer(),
Text(
order.orderNo,
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.8),
fontFamily: 'monospace',
),
),
],
),
const SizedBox(height: 20),
Text(
'¥${order.amount.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
order.productName,
style: TextStyle(
fontSize: 16,
color: Colors.white.withOpacity(0.9),
),
),
],
),
);
}
Widget _buildProductCard(order, AppLocalizations l10n) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l10n.productInfo,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 16),
Row(
children: [
Container(
width: 64,
height: 64,
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(Icons.image, color: AppColors.primary, size: 28),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
order.productName,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 6),
Text(
'${l10n.quantity}: x${order.quantity}',
style: TextStyle(
fontSize: 14,
color: AppColors.textSecondary,
),
),
const SizedBox(height: 4),
Text(
'${l10n.unitPrice}: ¥${(order.amount / order.quantity).toStringAsFixed(2)}',
style: TextStyle(
fontSize: 13,
color: AppColors.textTertiary,
),
),
],
),
),
],
),
],
),
);
}
Widget _buildCustomerCard(order, AppLocalizations l10n) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
),
child: Column(
children: [
_buildInfoRow(l10n.customerName, order.customerName, Icons.person_outline),
const Divider(height: 24),
_buildInfoRow(l10n.contactPhone, order.customerPhone, Icons.phone_outlined),
if (order.scenicSpot != null) ...[
const Divider(height: 24),
_buildInfoRow(l10n.belongScenic, order.scenicSpot!, Icons.place_outlined),
],
],
),
);
}
Widget _buildPaymentCard(order, AppLocalizations l10n) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l10n.paymentInfo,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 16),
_buildPaymentRow(l10n.orderAmount, '¥${order.amount.toStringAsFixed(2)}', isBold: true),
const SizedBox(height: 8),
_buildPaymentRow(l10n.actualAmount, '¥${order.amount.toStringAsFixed(2)}'),
if (order.paidAt != null) ...[
const SizedBox(height: 8),
_buildPaymentRow(
l10n.paymentTime,
'${order.paidAt!.month}/${order.paidAt!.day} ${order.paidAt!.hour.toString().padLeft(2, '0')}:${order.paidAt!.minute.toString().padLeft(2, '0')}',
),
],
],
),
);
}
Widget _buildPaymentRow(String label, String value, {bool isBold = false}) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: TextStyle(
fontSize: 14,
color: isBold ? AppColors.textPrimary : AppColors.textSecondary,
),
),
Text(
value,
style: TextStyle(
fontSize: 15,
fontWeight: isBold ? FontWeight.w600 : FontWeight.w400,
color: isBold ? AppColors.primary : AppColors.textPrimary,
),
),
],
);
}
Widget _buildInfoCard(order, AppLocalizations l10n) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l10n.orderInfo,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 16),
_buildInfoRow(l10n.orderNo, order.orderNo, Icons.tag),
const Divider(height: 24),
_buildInfoRow(l10n.createTime, _formatDateTime(order.createdAt), Icons.access_time),
if (order.verifiedAt != null) ...[
const Divider(height: 24),
_buildInfoRow(l10n.verifyTime, _formatDateTime(order.verifiedAt!), Icons.check_circle_outline),
],
if (order.refundedAt != null) ...[
const Divider(height: 24),
_buildInfoRow(l10n.refundTime, _formatDateTime(order.refundedAt!), Icons.replay),
],
if (order.verifyCode != null) ...[
const Divider(height: 24),
_buildInfoRow(l10n.verifyCode, order.verifyCode!, Icons.qr_code),
],
if (order.remark != null) ...[
const Divider(height: 24),
_buildInfoRow(l10n.remark, order.remark!, Icons.notes),
],
],
),
);
}
Widget _buildInfoRow(String label, String value, IconData icon) {
return Row(
children: [
Icon(icon, size: 18, color: AppColors.primary),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(fontSize: 12, color: AppColors.textTertiary),
),
const SizedBox(height: 2),
Text(
value,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: AppColors.textPrimary,
),
),
],
),
),
],
);
}
String _formatDateTime(DateTime dt) {
return '${dt.year}-${dt.month.toString().padLeft(2, '0')}-${dt.day.toString().padLeft(2, '0')} ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}';
}
}