import 'package:flutter/material.dart'; import 'package:pad_scanner/services/app_config_service.dart'; import 'package:pad_scanner/pages/settings_page.dart'; import 'package:pad_scanner/pages/registration_page.dart'; import 'package:pad_scanner/pages/boxing_page.dart'; import 'package:pad_scanner/services/api_service.dart'; /// Module type enum for each available feature card enum ModuleType { registration, boxing, shelfQuery } /// Module status for PRD state tracking enum ModuleStatus { online, developing, planning } /// Function card data model per PRD §3.3 class FeatureCard { final ModuleType type; final IconData icon; final String title; final String description; final ModuleStatus status; final String? version; final String route; const FeatureCard({ required this.type, required this.icon, required this.title, required this.description, required this.status, this.version, required this.route, }); } class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { final _apiService = ApiService(); bool _isCheckingConnection = false; bool _isConnected = false; String _apiAddress = ''; /// Module registry from PRD §3.4 static const _modules = [ FeatureCard( type: ModuleType.registration, icon: Icons.local_shipping_outlined, title: '上架登记', description: '扫码绑定总排号与货位', status: ModuleStatus.online, version: 'v1.0', route: '/registration', ), FeatureCard( type: ModuleType.boxing, icon: Icons.view_list_outlined, title: '装箱编号', description: '查询箱号、录入装箱明细', status: ModuleStatus.online, version: 'v1.0', route: '/boxing', ), FeatureCard( type: ModuleType.shelfQuery, icon: Icons.search_outlined, title: '货架查询', description: '按合同查询货架库存', status: ModuleStatus.planning, version: null, route: '', ), ]; @override void initState() { super.initState(); _checkConnection(); } Future _checkConnection() async { final configService = AppConfigService(); final url = await configService.getString('api_url') ?? ''; if (url.isEmpty) { setState(() { _isConnected = false; _apiAddress = ''; _isCheckingConnection = false; }); return; } setState(() => _isCheckingConnection = true); final ok = await _apiService.testConnection(url); if (!mounted) return; setState(() { _isConnected = ok; _apiAddress = url; _isCheckingConnection = false; }); } void _onCardTap(FeatureCard card) { switch (card.status) { case ModuleStatus.online: _navigateToModule(card.type); case ModuleStatus.developing: _showToast('该功能正在开发中'); case ModuleStatus.planning: _showToast('该功能规划中'); } } void _showToast(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message), duration: const Duration(seconds: 2)), ); } void _navigateToModule(ModuleType type) { late Widget targetPage; switch (type) { case ModuleType.registration: targetPage = const RegistrationPage(); break; case ModuleType.boxing: targetPage = const BoxingPage(); break; case ModuleType.shelfQuery: return; // 规划中,不导航 } Navigator.push( context, MaterialPageRoute(builder: (_) => targetPage), ).then((_) => _checkConnection()); } void _navigateToSettings() { Navigator.push( context, MaterialPageRoute(builder: (_) => const SettingsPage()), ).then((_) => _checkConnection()); } Color _getConnectionStatusColor() { if (_isCheckingConnection) return Colors.orange; return _isConnected ? Colors.green : Colors.red; } String _getConnectionStatusText() { if (_isCheckingConnection) return '● 正在检测连接...'; if (!_isConnected || _apiAddress.isEmpty) return '● 未连接,请检查设置'; return '● 已连接 $_apiAddress'; } @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Scaffold( appBar: AppBar( title: const Text('CargoTrace'), actions: [ Padding( padding: const EdgeInsets.only(right: 8), child: IconButton( icon: const Icon(Icons.settings), onPressed: _navigateToSettings, ), ), ], ), body: Column( children: [ // Function cards area Expanded( child: ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), itemCount: _modules.length, itemBuilder: (context, index) { final card = _modules[index]; return _buildFeatureCard(context, card); }, ), ), // Bottom connection status bar Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), color: colorScheme.surfaceContainerHighest, child: Row( children: [ Container( width: 8, height: 8, decoration: BoxDecoration( color: _getConnectionStatusColor(), shape: BoxShape.circle, ), ), const SizedBox(width: 8), Expanded( child: Text( _getConnectionStatusText(), style: const TextStyle(fontSize: 13), overflow: TextOverflow.ellipsis, ), ), ], ), ), ], ), ); } Widget _buildFeatureCard(BuildContext context, FeatureCard card) { final Color statusBgColor; final TextStyle statusTextStyle; final String statusText; switch (card.status) { case ModuleStatus.online: statusBgColor = Colors.green.shade700; statusTextStyle = const TextStyle(color: Colors.white, fontSize: 12); statusText = card.version ?? '已上线'; break; case ModuleStatus.developing: statusBgColor = Colors.blue.shade700; statusTextStyle = const TextStyle(color: Colors.white, fontSize: 12); statusText = '开发中'; break; case ModuleStatus.planning: statusBgColor = Colors.grey.shade500; statusTextStyle = const TextStyle(color: Colors.white, fontSize: 12); statusText = '规划中'; break; } final canNavigate = card.status == ModuleStatus.online; return Padding( padding: const EdgeInsets.only(bottom: 16), child: InkWell( onTap: () => _onCardTap(card), borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: canNavigate ? Colors.white : Colors.grey.shade200, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // First row: icon + module name Row( children: [ Icon( card.icon, size: 22, color: canNavigate ? Colors.blue : Colors.grey, ), const SizedBox(width: 8), Text( card.title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: canNavigate ? Colors.black87 : Colors.grey, ), ), ], ), const SizedBox(height: 6), // Second row: description Text( card.description, style: TextStyle(fontSize: 13, color: Colors.grey.shade600), ), const SizedBox(height: 10), // Third row: status badge (right aligned) Align( alignment: Alignment.centerRight, child: Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3, ), decoration: BoxDecoration( color: statusBgColor, borderRadius: BorderRadius.circular(4), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(statusText, style: statusTextStyle), if (card.status == ModuleStatus.online) ...[ const SizedBox(width: 6), Container( width: 6, height: 6, decoration: const BoxDecoration( color: Colors.white, shape: BoxShape.circle, ), ), ] else ...[ const SizedBox(width: 6), Container( width: 6, height: 6, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.5), shape: BoxShape.circle, ), ), ], ], ), ), ), ], ), ), ), ); } }