import 'dart:async'; import 'package:flutter/material.dart'; import 'agent.dart'; import 'server/protocol.dart'; /// 饭卡打印助手主窗口(Phase 1 最小版:服务状态 + 打印机状态 + 运行日志; /// 托盘/首次引导/驱动安装在后续 Phase 增强)。 Future main(List args) async { WidgetsFlutterBinding.ensureInitialized(); final agent = Agent(AgentOptions.parse(args)); String? startError; try { await agent.start(); } catch (e) { startError = '$e'; } runApp(DesktopPluginApp(agent: agent, startError: startError)); } class DesktopPluginApp extends StatelessWidget { const DesktopPluginApp({super.key, required this.agent, this.startError}); final Agent agent; final String? startError; @override Widget build(BuildContext context) { return MaterialApp( title: '饭卡打印助手', debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0F6FDE)), useMaterial3: true, ), home: AgentHomePage(agent: agent, startError: startError), ); } } class AgentHomePage extends StatefulWidget { const AgentHomePage({super.key, required this.agent, this.startError}); final Agent agent; final String? startError; @override State createState() => _AgentHomePageState(); } class _AgentHomePageState extends State { PrinterStatus? _status; Object? _statusError; bool _loading = false; StreamSubscription? _logSub; final _logs = []; @override void initState() { super.initState(); _logSub = widget.agent.log.stream.listen((line) { if (mounted) setState(() => _logs.add(line)); }); _logs.addAll(widget.agent.log.lines); _refreshStatus(); } @override void dispose() { _logSub?.cancel(); super.dispose(); } Future _refreshStatus() async { setState(() => _loading = true); try { final status = await widget.agent.backend.getStatus(); if (!mounted) return; setState(() { _status = status; _statusError = null; }); } catch (e) { if (!mounted) return; setState(() => _statusError = '$e'); } finally { if (mounted) setState(() => _loading = false); } } @override Widget build(BuildContext context) { final port = widget.agent.port; return Scaffold( appBar: AppBar( title: const Text('饭卡打印助手'), actions: [ IconButton(icon: const Icon(Icons.refresh), tooltip: '刷新状态', onPressed: _refreshStatus), const SizedBox(width: 8), ], ), body: ListView( padding: const EdgeInsets.all(16), children: [ _serviceCard(port), const SizedBox(height: 12), _printerCard(), const SizedBox(height: 12), _logCard(), ], ), ); } Widget _serviceCard(int? port) { final err = widget.startError; return Card( child: Padding( padding: const EdgeInsets.all(14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( err == null && port != null ? Icons.check_circle : Icons.error, color: err == null && port != null ? Colors.green : Colors.red, ), const SizedBox(width: 8), Text('本机服务', style: Theme.of(context).textTheme.titleMedium), ], ), const SizedBox(height: 8), Text(err ?? (port != null ? 'http://127.0.0.1:$port (管理端开卡页自动发现)' : '未启动')), const SizedBox(height: 4), Text('后端:${widget.agent.options.mock ? "mock(模拟)" : "系统打印队列"}'), ], ), ), ); } Widget _printerCard() { final status = _status; final err = _statusError; return Card( child: Padding( padding: const EdgeInsets.all(14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ if (_loading) const SizedBox(width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2)) else Icon( status != null && status.online ? Icons.print : Icons.print_disabled, color: status != null && status.online ? Colors.green : Colors.orange, ), const SizedBox(width: 8), Text('打印机', style: Theme.of(context).textTheme.titleMedium), ], ), const SizedBox(height: 8), if (err != null) Text('状态读取失败:$err', style: const TextStyle(color: Colors.red)) else if (status != null) ...[ Text('${status.model.isEmpty ? "未知型号" : status.model} · ${status.statusText}'), const SizedBox(height: 4), Text( '色带余量 ${status.ribbon.remain >= 0 ? "${status.ribbon.remain}%" : "未知"}' ' · 剩卡 ${status.cardLeft >= 0 ? "${status.cardLeft} 张" : "未知"}', style: TextStyle(color: Theme.of(context).hintColor), ), ], ], ), ), ); } Widget _logCard() { return Card( child: Padding( padding: const EdgeInsets.all(14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('运行日志', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), if (_logs.isEmpty) Text('(暂无)', style: TextStyle(color: Theme.of(context).hintColor)) else ...(_logs.length > 50 ? _logs.sublist(_logs.length - 50) : _logs) .map((line) => Text(line, style: const TextStyle(fontFamily: 'monospace', fontSize: 12))), ], ), ), ); } }