diff --git a/client/assets/config/app_info.json b/client/assets/config/app_info.json new file mode 100644 index 0000000..ad75e47 --- /dev/null +++ b/client/assets/config/app_info.json @@ -0,0 +1,5 @@ +{ + "provider": "岩美技术有限公司", + "website": "https://jiu.yanmei.com", + "email": "yammy2023@163.com" +} diff --git a/client/lib/core/config/app_info.dart b/client/lib/core/config/app_info.dart new file mode 100644 index 0000000..3c5808d --- /dev/null +++ b/client/lib/core/config/app_info.dart @@ -0,0 +1,28 @@ +import 'dart:convert'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +/// 应用「关于我们」等品牌信息,从配置文件 assets/config/app_info.json 读取, +/// 不硬编码、不走环境变量。在 main() 启动时调用 [load] 加载一次。 +class AppInfo { + const AppInfo._(); + + static const _assetPath = 'assets/config/app_info.json'; + + static String provider = ''; + static String website = ''; + static String email = ''; + + /// 加载配置文件。失败时保留为空(不影响应用启动)。 + static Future load() async { + try { + final raw = await rootBundle.loadString(_assetPath); + final map = jsonDecode(raw) as Map; + provider = (map['provider'] as String?) ?? ''; + website = (map['website'] as String?) ?? ''; + email = (map['email'] as String?) ?? ''; + } catch (e) { + debugPrint('AppInfo.load failed: $e'); + } + } +} diff --git a/client/lib/main.dart b/client/lib/main.dart index ee56097..bb1690d 100644 --- a/client/lib/main.dart +++ b/client/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_web_plugins/url_strategy.dart'; import 'core/auth/auth_state.dart'; +import 'core/config/app_info.dart'; import 'core/errors/error_reporter.dart'; import 'core/router/app_router.dart'; import 'core/theme/app_theme.dart'; @@ -24,7 +25,11 @@ void main() { }; runZonedGuarded( - () => runApp(const ProviderScope(child: JiuApp())), + () async { + WidgetsFlutterBinding.ensureInitialized(); + await AppInfo.load(); // 从配置文件加载「关于我们」品牌信息 + runApp(const ProviderScope(child: JiuApp())); + }, (error, stack) { debugPrint('═══ Zone Error ═══════════════════════════════'); debugPrint(error.toString()); diff --git a/client/lib/screens/settings/settings_screen.dart b/client/lib/screens/settings/settings_screen.dart index 8271627..ed78534 100644 --- a/client/lib/screens/settings/settings_screen.dart +++ b/client/lib/screens/settings/settings_screen.dart @@ -11,6 +11,7 @@ import 'package:intl/intl.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../core/auth/auth_state.dart'; import '../../core/config/app_config.dart'; +import '../../core/config/app_info.dart'; import '../../core/theme/app_theme.dart'; import '../../models/number_rule.dart'; import '../../models/user.dart'; @@ -828,9 +829,9 @@ class _SettingsScreenState extends ConsumerState { _AboutSection( title: '关于我们', children: [ - const _AboutRow(label: '开发商', value: '酒库科技有限公司'), - const _AboutRow(label: '官方网站', value: 'https://jiu.example.com'), - const _AboutRow(label: '联系邮箱', value: 'support@jiu.example.com'), + _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( @@ -838,8 +839,8 @@ class _SettingsScreenState extends ConsumerState { children: [ OutlinedButton.icon( onPressed: () async { - final uri = Uri.parse('mailto:support@jiu.example.com' - '?subject=酒库管理系统咨询'); + final uri = Uri.parse( + 'mailto:${AppInfo.email}?subject=酒库管理系统咨询'); if (await canLaunchUrl(uri)) launchUrl(uri); }, icon: const Icon(Icons.email_outlined, size: 16), @@ -887,17 +888,14 @@ class _SettingsScreenState extends ConsumerState { context: context, builder: (ctx) => AlertDialog( title: const Text('续费 / 升级授权'), - content: const Column( + content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('请联系我们获取续费报价:'), - SizedBox(height: 12), - SelectableText('📧 support@jiu.example.com', - style: TextStyle(fontSize: 13)), - SizedBox(height: 6), - SelectableText('📞 400-000-0000', - style: TextStyle(fontSize: 13)), + const Text('请联系我们获取续费报价:'), + const SizedBox(height: 12), + SelectableText('📧 ${AppInfo.email}', + style: const TextStyle(fontSize: 13)), ], ), actions: [ @@ -908,7 +906,7 @@ class _SettingsScreenState extends ConsumerState { ElevatedButton( onPressed: () async { await Clipboard.setData( - const ClipboardData(text: 'support@jiu.example.com')); + ClipboardData(text: AppInfo.email)); if (ctx.mounted) { Navigator.pop(ctx); ScaffoldMessenger.of(context).showSnackBar( @@ -952,7 +950,7 @@ class _SettingsScreenState extends ConsumerState { final subject = Uri.encodeComponent(isBug ? 'Bug反馈' : '功能建议'); final body = Uri.encodeComponent(ctrl.text); final uri = Uri.parse( - 'mailto:support@jiu.example.com?subject=$subject&body=$body'); + 'mailto:${AppInfo.email}?subject=$subject&body=$body'); if (await canLaunchUrl(uri)) launchUrl(uri); if (ctx.mounted) Navigator.pop(ctx); }, diff --git a/client/pubspec.yaml b/client/pubspec.yaml index 91b7637..a8c0819 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -37,3 +37,4 @@ flutter: assets: - assets/fonts/NotoSansSC-Regular.ttf + - assets/config/app_info.json diff --git a/client/windows/runner/main.cpp b/client/windows/runner/main.cpp index cbba692..6a9f59b 100644 --- a/client/windows/runner/main.cpp +++ b/client/windows/runner/main.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include "flutter_window.h" #include "utils.h" @@ -27,7 +29,16 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); - if (!window.Create(L"jiu_client", origin, size)) { + // 窗口标题:用 u8 字面量 + UTF-8→UTF-16 转换,避免源码编码导致中文乱码。 + const std::string title_utf8 = u8"岩美酒库"; + int title_len = ::MultiByteToWideChar(CP_UTF8, 0, title_utf8.c_str(), -1, + nullptr, 0); + std::wstring window_title(title_len > 0 ? title_len - 1 : 0, L'\0'); + if (title_len > 0) { + ::MultiByteToWideChar(CP_UTF8, 0, title_utf8.c_str(), -1, &window_title[0], + title_len); + } + if (!window.Create(window_title, origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true);