import 'package:flutter/material.dart'; import '../services/config_service.dart'; import '../utils/ini_utils.dart'; class ConfigEditorScreen extends StatefulWidget { const ConfigEditorScreen({super.key}); @override State createState() => _ConfigEditorScreenState(); } class _ConfigEditorScreenState extends State { final ConfigService _configService = ConfigService(); bool _isLoading = false; bool _isModified = false; // 配置项数据 final Map> _controllers = {}; final Map> _enabledFlags = {}; final List _sections = []; @override void initState() { super.initState(); _loadConfig(); } Future _loadConfig() async { setState(() { _isLoading = true; }); try { final content = await _configService.readConfig(); if (content.isNotEmpty) { _parseConfig(content); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('加载配置失败: $e')), ); } } finally { setState(() { _isLoading = false; }); } } void _parseConfig(String content) { final config = parseIni(content); _controllers.clear(); _enabledFlags.clear(); _sections.clear(); // 定义所有可能的配置项 final configStructure = { 'client': ['server_url', 'update_url'], 'lolminer': ['path'], 'rigel': ['path'], 'bzminer': ['path'], 'proxy': ['proxy'], 'sustain': ['enabled', 'algo', 'coin', 'miner', 'pool_url', 'wallet', 'worker_id', 'pool_user', 'wallet_mining'], }; for (final section in configStructure.keys) { _sections.add(section); _controllers[section] = {}; _enabledFlags[section] = {}; for (final option in configStructure[section]!) { final value = config.get(section, option) ?? ''; _controllers[section]![option] = TextEditingController(text: value); _controllers[section]![option]!.addListener(() { setState(() { _isModified = true; }); }); // 默认所有配置项都是禁用的(需要勾选才能编辑) _enabledFlags[section]![option] = false; } } } Future _saveConfig() async { final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('确认保存'), content: const Text('确定要保存配置文件吗?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('取消'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('确定'), ), ], ), ); if (confirmed == true) { setState(() { _isLoading = true; }); try { final content = _generateConfigContent(); final success = await _configService.saveConfig(content); if (mounted) { setState(() { _isLoading = false; _isModified = !success; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(success ? '保存成功' : '保存失败'), backgroundColor: success ? Colors.green : Colors.red, ), ); } } catch (e) { if (mounted) { setState(() { _isLoading = false; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('保存失败: $e')), ); } } } } String _generateConfigContent() { final buffer = StringBuffer(); // 添加注释 buffer.writeln('#请确认您的主机上安装了下列挖矿软件,确认后可以打开注释,并修改其路径,如果没有安装,请勿打开注释'); buffer.writeln('#请使用双\\\\,否则可能无法解析出准确的路径'); buffer.writeln(); for (final section in _sections) { if (section == 'client') { buffer.writeln('[client]'); _writeOption(buffer, section, 'server_url'); _writeOption(buffer, section, 'update_url'); buffer.writeln(); buffer.writeln('#请确认您的主机上安装了下列挖矿软件,确认后可以打开注释,并修改其路径,如果没有安装,请勿打开注释'); buffer.writeln('#请使用双\\\\,否则可能无法解析出准确的路径'); } else if (section == 'lolminer' || section == 'rigel' || section == 'bzminer') { buffer.writeln('[$section]'); _writeOption(buffer, section, 'path', commentPrefix: '# '); } else if (section == 'proxy') { buffer.writeln(); buffer.writeln('#如果您的网络无法直接连通各个矿池,需要使用各大矿池专用网络,请打开proxy的注释'); buffer.writeln('#打开此注释后会使用各大矿池的专用网络,每笔订单额外增加1%的网络费用'); buffer.writeln('[proxy]'); _writeOption(buffer, section, 'proxy', commentPrefix: '# '); } else if (section == 'sustain') { buffer.writeln(); buffer.writeln('#持续挖矿开关,即在矿机没有租约期间是否自行挖矿'); buffer.writeln('#开启此选项启动客户端后,客户端会自动根据下面配置开启挖矿任务,直到云算力平台有人租赁本台GPU主机'); buffer.writeln('#当该租约结束后,本台GPU主机会自动切回下方配置的挖矿任务'); buffer.writeln('[sustain]'); _writeOption(buffer, section, 'enabled', commentPrefix: '#'); _writeOption(buffer, section, 'algo', commentPrefix: '#'); _writeOption(buffer, section, 'coin', commentPrefix: '#'); _writeOption(buffer, section, 'miner', commentPrefix: '#'); _writeOption(buffer, section, 'pool_url', commentPrefix: '#'); _writeOption(buffer, section, 'wallet', commentPrefix: '#'); _writeOption(buffer, section, 'worker_id', commentPrefix: '#'); _writeOption(buffer, section, 'pool_user', commentPrefix: '#'); _writeOption(buffer, section, 'wallet_mining', commentPrefix: '#'); } buffer.writeln(); } return buffer.toString(); } void _writeOption(StringBuffer buffer, String section, String option, {String commentPrefix = ''}) { final controller = _controllers[section]?[option]; final enabled = _enabledFlags[section]?[option] ?? false; final value = controller?.text ?? ''; if (value.isEmpty || !enabled) { // 如果值为空或未启用,使用注释形式 if (option == 'path') { buffer.writeln('$commentPrefix$option=C:\\\\path\\\\${section}'); } else if (option == 'proxy') { buffer.writeln('$commentPrefix$option=true'); } else if (option == 'enabled') { buffer.writeln('$commentPrefix$option=true'); } else if (option == 'algo') { buffer.writeln('$commentPrefix$option="算法"'); } else if (option == 'coin') { buffer.writeln('$commentPrefix$option="币种"'); } else if (option == 'miner') { buffer.writeln('$commentPrefix$option="挖矿软件名,此处使用的挖矿软件要使用上方已经配置路径的挖矿软件名,即bzminer/lolminer/rigel,只能填一个,自行选择"'); } else if (option == 'pool_url') { buffer.writeln('$commentPrefix$option="挖矿地址"'); } else if (option == 'wallet') { buffer.writeln('$commentPrefix$option="挖矿钱包"'); } else if (option == 'worker_id') { buffer.writeln('$commentPrefix$option="矿工号"'); } else if (option == 'pool_user') { buffer.writeln('$commentPrefix$option="挖矿账号名,f2pool/m2pool等不支持钱包挖矿的矿池需配置,其余支持钱包挖矿的矿池无需配置"'); } else if (option == 'wallet_mining') { buffer.writeln('$commentPrefix$option=true #pool_user打开时同时打开本配置'); } else { buffer.writeln('$commentPrefix$option=$value'); } } else { // 如果值不为空且已启用,写入实际值 if (option == 'path' || option == 'server_url' || option == 'update_url' || option == 'pool_url' || option == 'wallet' || option == 'worker_id' || option == 'pool_user' || option == 'algo' || option == 'coin' || option == 'miner') { buffer.writeln('$option=$value'); } else { buffer.writeln('$option=$value'); } } } @override void dispose() { for (final section in _controllers.values) { for (final controller in section.values) { controller.dispose(); } } super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('配置文件编辑'), actions: [ if (_isModified) IconButton( icon: const Icon(Icons.save), onPressed: _isLoading ? null : _saveConfig, tooltip: '保存', ), ], ), body: _isLoading && _controllers.isEmpty ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (_isModified) Container( padding: const EdgeInsets.all(12.0), margin: const EdgeInsets.only(bottom: 16.0), decoration: BoxDecoration( color: Colors.orange.shade50, borderRadius: BorderRadius.circular(8.0), border: Border.all(color: Colors.orange), ), child: Row( children: [ Icon(Icons.info, color: Colors.orange.shade700), const SizedBox(width: 8), const Expanded( child: Text( '配置已修改,请点击保存按钮保存更改', style: TextStyle(fontWeight: FontWeight.w500), ), ), ], ), ), ..._sections.map((section) => _buildSection(section)), const SizedBox(height: 16), ElevatedButton.icon( onPressed: _isLoading || !_isModified ? null : _saveConfig, icon: const Icon(Icons.save), label: const Text('保存配置'), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), ), ), ], ), ), ); } Widget _buildSection(String section) { final sectionNames = { 'client': '客户端配置', 'lolminer': 'LolMiner 配置', 'rigel': 'Rigel 配置', 'bzminer': 'BzMiner 配置', 'proxy': '代理配置', 'sustain': '持续挖矿配置', }; return Card( margin: const EdgeInsets.only(bottom: 16.0), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( sectionNames[section] ?? section, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const Divider(), ...(_controllers[section]?.keys.map((option) => _buildConfigField(section, option)) ?? []), ], ), ), ); } Widget _buildConfigField(String section, String option) { final controller = _controllers[section]?[option]; final enabled = _enabledFlags[section]?[option] ?? false; final fieldNames = { 'server_url': '服务器地址', 'update_url': '更新服务器地址', 'path': '软件路径', 'proxy': '启用代理', 'enabled': '启用持续挖矿', 'algo': '算法', 'coin': '币种', 'miner': '挖矿软件', 'pool_url': '矿池地址', 'wallet': '钱包地址', 'worker_id': '矿工号', 'pool_user': '矿池用户名', 'wallet_mining': '钱包挖矿', }; return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Checkbox( value: enabled, onChanged: (value) { setState(() { _enabledFlags[section]![option] = value ?? false; _isModified = true; }); }, ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( fieldNames[option] ?? option, style: const TextStyle(fontWeight: FontWeight.w500), ), const SizedBox(height: 4), TextField( controller: controller, enabled: enabled, decoration: InputDecoration( border: const OutlineInputBorder(), hintText: '请输入${fieldNames[option] ?? option}', filled: !enabled, fillColor: enabled ? null : Colors.grey.shade200, ), ), ], ), ), ], ), ); } }