Files
zhinian_manage/lib/pages/scan_result_page.dart

426 lines
14 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:url_launcher/url_launcher.dart';
import '../providers/order_provider.dart';
import '../theme.dart';
import '../l10n/app_localizations.dart';
class ScanResultPage extends ConsumerStatefulWidget {
final String scanCode;
const ScanResultPage({super.key, required this.scanCode});
@override
ConsumerState<ScanResultPage> createState() => _ScanResultPageState();
}
class _ScanResultPageState extends ConsumerState<ScanResultPage> {
bool _showActionSheet = false;
@override
void initState() {
super.initState();
Future.microtask(() {
ref.read(orderProvider.notifier).scanVerify(widget.scanCode);
});
}
void _showVerifyDialog() {
final l10n = AppLocalizations.of(context)!;
final order = ref.read(orderProvider).currentOrder;
if (order == null || !order.canVerify) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(l10n.notVerifiable)),
);
return;
}
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
isScrollControlled: true,
builder: (context) {
return Container(
padding: const EdgeInsets.all(24),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: AppColors.divider,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(height: 24),
Container(
width: 64,
height: 64,
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.1),
shape: BoxShape.circle,
),
child: const Icon(Icons.qr_code_scanner, color: AppColors.primary, size: 32),
),
const SizedBox(height: 20),
Text(
l10n.confirmVerify,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 8),
Text(
'${l10n.orderNo}: ${order.orderNo}',
style: TextStyle(fontSize: 14, color: AppColors.textSecondary),
),
const SizedBox(height: 4),
Text(
'${l10n.customerName}: ${order.customerName}',
style: TextStyle(fontSize: 14, color: AppColors.textSecondary),
),
const SizedBox(height: 4),
Text(
'${l10n.amount}: ¥${order.amount.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
const SizedBox(height: 24),
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.of(context).pop(),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: BorderSide(color: AppColors.divider),
),
child: Text(l10n.cancel),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_doVerify();
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Text(l10n.confirmVerify),
),
),
],
),
],
),
),
);
},
);
}
void _doVerify() {
final l10n = AppLocalizations.of(context)!;
final order = ref.read(orderProvider).currentOrder;
if (order == null) return;
ref.read(orderProvider.notifier).verifyOrder(order.id).then((_) {
final state = ref.read(orderProvider);
if (state.verifySuccess) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(l10n.verifySuccess),
backgroundColor: AppColors.success,
behavior: SnackBarBehavior.floating,
),
);
ref.read(orderProvider.notifier).clearVerifySuccess();
setState(() => _showActionSheet = false);
}
});
}
Future<void> _makePhoneCall() async {
final order = ref.read(orderProvider).currentOrder;
if (order == null) return;
final phone = order.customerPhone.replaceAll('*', '0');
final uri = Uri.parse('tel:$phone');
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
}
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final state = ref.watch(orderProvider);
final order = state.currentOrder;
if (state.isLoading || order == null) {
return Scaffold(
backgroundColor: AppColors.background,
appBar: AppBar(title: Text(l10n.scanResult)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 16),
Text(l10n.scanning, style: TextStyle(color: AppColors.textSecondary)),
],
),
),
);
}
return Scaffold(
backgroundColor: AppColors.background,
appBar: AppBar(
title: Text(l10n.scanResult),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () => context.go('/home'),
),
),
body: Stack(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildResultHeader(order, l10n),
const SizedBox(height: 20),
_buildOrderInfoCard(order, l10n),
const SizedBox(height: 20),
_buildCustomerCard(order, l10n),
const SizedBox(height: 100),
],
),
),
if (order.canVerify)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: _buildBottomActions(order, l10n),
),
],
),
);
}
Widget _buildResultHeader(order, AppLocalizations l10n) {
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 SizedBox(height: 16),
Text(
l10n.scanSuccess,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
'${l10n.orderNo}: ${order.orderNo}',
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.85),
fontFamily: 'monospace',
),
),
],
),
);
}
Widget _buildOrderInfoCard(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.orderInfo,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 16),
_buildInfoRow(l10n.product, order.productName, Icons.shopping_bag_outlined),
const Divider(height: 20),
_buildInfoRow(l10n.quantity, 'x${order.quantity}', Icons.confirmation_number_outlined),
const Divider(height: 20),
_buildInfoRow(l10n.amount, '¥${order.amount.toStringAsFixed(2)}', Icons.payments_outlined),
if (order.verifyCode != null) ...[
const Divider(height: 20),
_buildInfoRow(l10n.verifyCode, order.verifyCode!, Icons.qr_code),
],
],
),
);
}
Widget _buildCustomerCard(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.customerInfo,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 16),
_buildInfoRow(l10n.name, order.customerName, Icons.person_outline),
const Divider(height: 20),
_buildInfoRow(l10n.phone, order.customerPhone, Icons.phone_outlined),
],
),
);
}
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,
),
),
],
),
),
],
);
}
Widget _buildBottomActions(order, AppLocalizations l10n) {
return Container(
padding: const EdgeInsets.fromLTRB(20, 16, 20, 28),
decoration: BoxDecoration(
color: AppColors.surface,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 20,
offset: const Offset(0, -4),
),
],
),
child: SafeArea(
child: Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: _makePhoneCall,
icon: const Icon(Icons.phone, size: 20),
label: Text(l10n.contactCustomer),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton.icon(
onPressed: _showVerifyDialog,
icon: const Icon(Icons.check_circle, size: 20),
label: Text(l10n.verify),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.success,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
),
),
),
],
),
),
);
}
}