import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../core/config/app_info.dart'; import '../../core/theme/app_theme.dart'; import '../../core/update/app_updater.dart'; import '../../core/utils/dialog_util.dart'; import '../../providers/license_provider.dart'; import '../../providers/update_provider.dart'; /// 「关于我们」独立页面(左侧菜单项)。原为系统设置里的「关于」Tab。 class AboutScreen extends ConsumerStatefulWidget { const AboutScreen({super.key}); @override ConsumerState createState() => _AboutScreenState(); } class _AboutScreenState extends ConsumerState { @override Widget build(BuildContext context) { final appVersion = ref.watch(appVersionProvider).valueOrNull ?? 'v1.0.0'; final updateInfo = ref.watch(updateProvider).valueOrNull; final licenseAsync = ref.watch(licenseProvider); return SingleChildScrollView( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ── 版本信息 ── _AboutSection( title: '版本信息', children: [ _AboutRow(label: '当前版本', value: appVersion), if (updateInfo != null && updateInfo.hasUpdate) _AboutRow( label: '最新版本', value: 'v${updateInfo.latestVersion}', valueColor: AppTheme.success, trailing: TextButton( onPressed: () => startInAppUpdate(context, updateInfo), child: const Text('立即更新'), ), ) else _AboutRow( label: '最新版本', value: updateInfo != null ? '已是最新' : '检查中…', valueColor: AppTheme.textSecondary, trailing: TextButton( onPressed: () async { final messenger = ScaffoldMessenger.of(context); messenger.showSnackBar( const SnackBar(content: Text('正在检查更新…'))); await ref.read(updateProvider.notifier).forceCheck(); if (!context.mounted) return; final r = ref.read(updateProvider).valueOrNull; messenger.hideCurrentSnackBar(); final String msg; if (r == null) { msg = '检查更新失败,请检查网络'; } else if (r.hasUpdate) { msg = '发现新版本 v${r.latestVersion}'; } else { msg = '已是最新版本(v${r.latestVersion})'; } messenger.showSnackBar(SnackBar(content: Text(msg))); }, child: const Text('检查更新'), ), ), ], ), const SizedBox(height: 20), // ── 授权信息 ── _AboutSection( title: '授权信息', children: [ licenseAsync.when( loading: () => const _AboutRow(label: '授权状态', value: '加载中…'), error: (_, __) => const _AboutRow(label: '授权状态', value: '暂无授权信息'), data: (lic) { if (lic == null) { return const _AboutRow(label: '授权状态', value: '未激活'); } return Column( children: [ _AboutRow(label: '授权类型', value: lic.typeLabel), _AboutRow( label: '授权状态', value: lic.isExpired ? '已过期' : lic.isActive ? '正常' : '已停用', valueColor: lic.isExpired ? AppTheme.danger : lic.isActive ? AppTheme.success : AppTheme.textSecondary, ), if (lic.expiresAt != null) _AboutRow( label: '到期时间', value: DateFormat('yyyy-MM-dd').format(lic.expiresAt!), trailing: lic.daysRemaining != null && lic.daysRemaining! <= 30 ? Chip( label: Text( lic.isExpired ? '已过期' : '剩余 ${lic.daysRemaining} 天', style: const TextStyle( fontSize: 11, color: Colors.white), ), backgroundColor: lic.isExpired ? AppTheme.danger : Colors.orange, padding: EdgeInsets.zero, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ) : null, ) else const _AboutRow(label: '到期时间', value: '永久有效'), if (lic.activatedAt != null) _AboutRow( label: '激活时间', value: DateFormat('yyyy-MM-dd') .format(lic.activatedAt!), ), ], ); }, ), const SizedBox(height: 8), Row( children: [ OutlinedButton.icon( onPressed: () => _showRenewDialog(), icon: const Icon(Icons.card_membership, size: 16), label: const Text('续费 / 升级授权'), ), ], ), ], ), const SizedBox(height: 20), // ── 关于我们 ── _AboutSection( title: '关于我们', children: [ _AboutRow(label: '服务商', value: AppInfo.provider), _AboutRow(label: '官方网站', value: AppInfo.website), _AboutRow(label: '联系邮箱', value: AppInfo.email), const _AboutRow(label: '技术支持', value: '周一至周五 9:00 - 18:00'), const SizedBox(height: 8), Wrap( spacing: 8, children: [ OutlinedButton.icon( onPressed: () async { final uri = Uri.parse( 'mailto:${AppInfo.email}?subject=酒库管理系统咨询'); if (await canLaunchUrl(uri)) launchUrl(uri); }, icon: const Icon(Icons.email_outlined, size: 16), label: const Text('发送邮件'), ), ], ), ], ), const SizedBox(height: 20), // ── 意见反馈 ── _AboutSection( title: '意见反馈', children: [ const _AboutRow( label: '问题反馈', value: '遇到 Bug 或有功能建议,欢迎告知我们', ), const SizedBox(height: 8), Wrap( spacing: 8, children: [ ElevatedButton.icon( onPressed: () => _showFeedbackDialog(isBug: true), icon: const Icon(Icons.bug_report_outlined, size: 16), label: const Text('反馈 Bug'), ), OutlinedButton.icon( onPressed: () => _showFeedbackDialog(isBug: false), icon: const Icon(Icons.lightbulb_outline, size: 16), label: const Text('功能建议'), ), ], ), ], ), ], ), ); } void _showRenewDialog() { showAppDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('续费 / 升级授权'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('请联系我们获取续费报价:'), const SizedBox(height: 12), SelectableText('📧 ${AppInfo.email}', style: const TextStyle(fontSize: 13)), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text('关闭'), ), ElevatedButton( onPressed: () async { await Clipboard.setData(ClipboardData(text: AppInfo.email)); if (ctx.mounted) { Navigator.pop(ctx); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('邮箱已复制到剪贴板')), ); } }, child: const Text('复制邮箱'), ), ], ), ); } void _showFeedbackDialog({required bool isBug}) { final ctrl = TextEditingController(); showAppDialog( context: context, builder: (ctx) => AlertDialog( title: Text(isBug ? '反馈 Bug' : '功能建议'), content: SizedBox( width: 400, child: TextField( controller: ctrl, maxLines: 6, decoration: InputDecoration( hintText: isBug ? '请描述问题的复现步骤和预期行为…' : '请描述您希望增加的功能…', border: const OutlineInputBorder(), ), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text('取消'), ), ElevatedButton( onPressed: () async { final subject = Uri.encodeComponent(isBug ? 'Bug反馈' : '功能建议'); final body = Uri.encodeComponent(ctrl.text); final uri = Uri.parse( 'mailto:${AppInfo.email}?subject=$subject&body=$body'); if (await canLaunchUrl(uri)) launchUrl(uri); if (ctx.mounted) Navigator.pop(ctx); }, child: const Text('通过邮件发送'), ), ], ), ); } } // ── 关于页辅助 widgets ────────────────────────────────────── class _AboutSection extends StatelessWidget { final String title; final List children; const _AboutSection({required this.title, required this.children}); @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: AppTheme.primaryDark)), const Divider(height: 24), ...children, ], ), ), ); } } class _AboutRow extends StatelessWidget { final String label; final String value; final Color? valueColor; final Widget? trailing; const _AboutRow({ required this.label, required this.value, this.valueColor, this.trailing, }); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: Row( children: [ SizedBox( width: 88, child: Text(label, style: const TextStyle( fontSize: 13, color: AppTheme.textSecondary)), ), Expanded( child: Text(value, style: TextStyle( fontSize: 13, color: valueColor ?? AppTheme.textPrimary, fontWeight: FontWeight.w500)), ), if (trailing != null) trailing!, ], ), ); } }