import 'package:flutter/material.dart'; import 'package:flutter/services.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 String shortcut; final IconData icon; final String title; final ModuleStatus status; final String route; const FeatureCard({ required this.type, required this.shortcut, required this.icon, required this.title, required this.status, required this.route, }); } class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { final _apiService = ApiService(); final _focusNode = FocusNode(); bool _isCheckingConnection = false; bool _isConnected = false; String _apiAddress = ''; /// Module registry from PRD §3.4 static const _modules = [ FeatureCard( type: ModuleType.registration, shortcut: '1', icon: Icons.local_shipping_outlined, title: '上架登记', status: ModuleStatus.online, route: '/registration', ), FeatureCard( type: ModuleType.boxing, shortcut: '2', icon: Icons.view_list_outlined, title: '装箱编号', status: ModuleStatus.online, route: '/boxing', ), FeatureCard( type: ModuleType.shelfQuery, shortcut: '3', icon: Icons.search_outlined, title: '货架查询', status: ModuleStatus.planning, route: '', ), ]; @override void initState() { super.initState(); _checkConnection(); WidgetsBinding.instance.addPostFrameCallback((_) => _requestFocus()); } @override void dispose() { _focusNode.dispose(); super.dispose(); } void _requestFocus() { if (mounted) { _focusNode.requestFocus(); } } void _onKeyEvent(KeyEvent event) { if (event is! KeyDownEvent) return; final module = _moduleFromShortcut(event); if (module == null) return; _onCardTap(module); } FeatureCard? _moduleFromShortcut(KeyEvent event) { final shortcut = _shortcutNumberFromKeyEvent(event); if (shortcut == 1) { return _modules[0]; } if (shortcut == 2) { return _modules[1]; } if (shortcut == 3) { return _modules[2]; } return null; } int? _shortcutNumberFromKeyEvent(KeyEvent event) { final key = event.logicalKey; if (key == LogicalKeyboardKey.digit1 || key == LogicalKeyboardKey.numpad1 || event.character == '1') { return 1; } if (key == LogicalKeyboardKey.digit2 || key == LogicalKeyboardKey.numpad2 || event.character == '2') { return 2; } if (key == LogicalKeyboardKey.digit3 || key == LogicalKeyboardKey.numpad3 || event.character == '3') { return 3; } return null; } 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(); _requestFocus(); }, ); } void _navigateToSettings() { Navigator.push( context, MaterialPageRoute(builder: (_) => const SettingsPage()), ).then((_) { _checkConnection(); _requestFocus(); }); } 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 KeyboardListener( focusNode: _focusNode, onKeyEvent: _onKeyEvent, child: Scaffold( appBar: AppBar( title: const SizedBox.shrink(), toolbarHeight: 44, 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.fromLTRB(16, 14, 16, 4), 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 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(14), 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: Row( children: [ Icon( card.icon, size: 22, color: canNavigate ? Colors.blue : Colors.grey, ), const SizedBox(width: 8), Text( card.title, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: canNavigate ? Colors.black87 : Colors.grey, ), ), const Spacer(), _buildShortcutHint(card, canNavigate), ], ), ), ), ); } Widget _buildShortcutHint(FeatureCard card, bool canNavigate) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: canNavigate ? Colors.blue.shade50 : Colors.grey.shade300, border: Border.all( color: canNavigate ? Colors.blue.shade200 : Colors.grey.shade400, ), borderRadius: BorderRadius.circular(6), ), child: Text( '按 ${card.shortcut} 进入', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w700, color: canNavigate ? Colors.blue.shade800 : Colors.grey.shade600, ), ), ); } }