diff --git a/ln_jq_app/lib/common/styles/theme.dart b/ln_jq_app/lib/common/styles/theme.dart index bf760ea..767c678 100644 --- a/ln_jq_app/lib/common/styles/theme.dart +++ b/ln_jq_app/lib/common/styles/theme.dart @@ -7,9 +7,12 @@ class AppTheme { static const Color themeColor = Color(0xFF0c83c3); + //是否开放域名切换 + static const bool is_show_host = true; + //http://192.168.110.222:8080/ //http://192.168.110.44:8080/ - static const String test_service_url = "https://beta-esg.api.lnh2e.com/"; + static String test_service_url = "https://beta-esg.api.lnh2e.com/"; static const String release_service_url = ""; //加氢站相关查询 @@ -23,10 +26,10 @@ class AppTheme { static const Color darkThemeColor = Color(0xFF032896); - static const String android_key ="335642645"; - static const String android_appsecret="39628204345a4240b5b645b68a5896c7"; - static const String ios_key="335642649"; - static const String ios_appsecret="173bc08bd5df422da20c8e3ffbf0521b"; + static const String android_key = "335642645"; + static const String android_appsecret = "39628204345a4240b5b645b68a5896c7"; + static const String ios_key = "335642649"; + static const String ios_appsecret = "173bc08bd5df422da20c8e3ffbf0521b"; /// 亮色主题样式 static ThemeData light = ThemeData( diff --git a/ln_jq_app/lib/common/token_interceptor.dart b/ln_jq_app/lib/common/token_interceptor.dart index fe15a64..dbb5562 100644 --- a/ln_jq_app/lib/common/token_interceptor.dart +++ b/ln_jq_app/lib/common/token_interceptor.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; +import 'package:getx_scaffold/getx_scaffold.dart'; import 'package:ln_jq_app/storage_service.dart'; /// 专门用于处理和添加 Token 的拦截器 @@ -13,7 +14,7 @@ class TokenInterceptor extends Interceptor { TokenInterceptor({this.tokenKey = 'Authorization', this.sourceKey = 'source'}); @override - void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + void onRequest(RequestOptions options, RequestInterceptorHandler handler) async{ // 从 StorageService 中获取已保存的 token final String? token = StorageService.to.token; @@ -38,6 +39,9 @@ class TokenInterceptor extends Interceptor { options.headers[sourceKey] = platformSource; } + options.headers['appVersion'] = await getVersion(); + options.headers['brand'] = await getDeviceModel(); + // 调用 handler.next(options) 以继续执行请求 // 这一步至关重要,否则请求会被中断 super.onRequest(options, handler); diff --git a/ln_jq_app/lib/main.dart b/ln_jq_app/lib/main.dart index 31692b4..5710b26 100644 --- a/ln_jq_app/lib/main.dart +++ b/ln_jq_app/lib/main.dart @@ -65,6 +65,8 @@ void main() async { } void initHttpSet() { + AppTheme.test_service_url = StorageService.to.hostUrl ?? AppTheme.test_service_url; + // 设置基础 URL HttpService.to.setBaseUrl(AppTheme.test_service_url); //指定请求头 diff --git a/ln_jq_app/lib/pages/login/view.dart b/ln_jq_app/lib/pages/login/view.dart index ca5beab..63b24de 100644 --- a/ln_jq_app/lib/pages/login/view.dart +++ b/ln_jq_app/lib/pages/login/view.dart @@ -1,5 +1,7 @@ import 'package:aliyun_push_flutter/aliyun_push_flutter.dart'; import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:get/get.dart'; import 'package:getx_scaffold/getx_scaffold.dart'; import 'package:ln_jq_app/common/login_util.dart'; import 'package:ln_jq_app/common/model/base_model.dart'; @@ -8,6 +10,7 @@ import 'package:ln_jq_app/common/styles/theme.dart'; import 'package:ln_jq_app/pages/b_page/base_widgets/view.dart'; import 'package:ln_jq_app/pages/c_page/base_widgets/view.dart'; import 'package:ln_jq_app/pages/login/controller.dart'; +import 'package:ln_jq_app/pages/url_host/view.dart'; import 'package:ln_jq_app/storage_service.dart'; class LoginPage extends StatefulWidget { @@ -414,7 +417,31 @@ class _LoginPageState extends State with SingleTickerProviderStateMix init: LoginController(), id: 'login', builder: (controller) { - return Scaffold(body: _buildView(controller)); + return Scaffold( + body: Stack( + children: [ + Positioned.fill(child: _buildView(controller)), + if (AppTheme.is_show_host) + Positioned( + top: 40.h, + right: 20.w, + child: TextButton( + onPressed: () { + Get.to(() => const UrlHostPage()); + }, + child: const Text( + "域名配置", + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ); }, ); } diff --git a/ln_jq_app/lib/pages/url_host/controller.dart b/ln_jq_app/lib/pages/url_host/controller.dart new file mode 100644 index 0000000..4f1d78a --- /dev/null +++ b/ln_jq_app/lib/pages/url_host/controller.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:getx_scaffold/getx_scaffold.dart'; +import 'package:ln_jq_app/common/styles/theme.dart'; +import 'package:ln_jq_app/storage_service.dart'; + +class UrlHostController extends GetxController { + final TextEditingController urlController = TextEditingController(); + + // 预设的域名列表 + final List presetUrls = [ + 'https://beta-esg.api.lnh2e.com/', // 测试环境 + 'http://192.168.110.44:8080/', // 沈辰本地 + 'http://192.168.110.222:8080/', // 何斐本地 + ]; + + final List urlNames = [ + '测试环境', + '沈辰本地环境', + '何斐本地环境', + ]; + + @override + void onInit() { + super.onInit(); + // 初始化时,尝试从 StorageService 获取已保存的域名 + // 如果没有保存过,则使用当前的全局配置 + String? savedUrl = StorageService.to.hostUrl; + if (savedUrl != null && savedUrl.isNotEmpty) { + urlController.text = savedUrl; + } else { + urlController.text = AppTheme.test_service_url; + } + } + + /// 当用户点击列表中的某一项时,将其填入编辑框 + void selectUrl(String url) { + urlController.text = url; + } + + /// 保存配置并关闭页面 + void saveAndExit() async { + String newUrl = urlController.text.trim(); + if (newUrl.isEmpty) { + showToast('请输入有效的域名'); + return; + } + + // 保存到本地存储,以便下次启动时加载 + await StorageService.to.saveHostUrl(newUrl); + AppTheme.test_service_url = newUrl; + + //设置框架 + HttpService.to.setBaseUrl(AppTheme.test_service_url); + + showSuccessToast('域名已更新:${AppTheme.test_service_url}'); + Get.back(); + } + + @override + void onClose() { + urlController.dispose(); + super.onClose(); + } +} diff --git a/ln_jq_app/lib/pages/url_host/view.dart b/ln_jq_app/lib/pages/url_host/view.dart new file mode 100644 index 0000000..6d2ff57 --- /dev/null +++ b/ln_jq_app/lib/pages/url_host/view.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:getx_scaffold/getx_scaffold.dart'; + +import 'controller.dart'; + +class UrlHostPage extends GetView { + const UrlHostPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + Get.put(UrlHostController()); + + return Scaffold( + appBar: AppBar( + title: const Text('域名配置'), + centerTitle: true, + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text( + '当前环境配置', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 12), + TextField( + controller: controller.urlController, + decoration: InputDecoration( + hintText: '请输入或选择API域名', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), + ), + ), + const SizedBox(height: 24), + const Text( + '预设环境', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + Expanded( + child: ListView.builder( + itemCount: controller.presetUrls.length, + itemBuilder: (context, index) { + final url = controller.presetUrls[index]; + return Card( + elevation: 1, + margin: const EdgeInsets.only(bottom: 8), + child: ListTile( + title: Text(controller.urlNames[index], style: const TextStyle(fontSize: 14)), + subtitle: Text(url, style: const TextStyle(fontSize: 14)), + trailing: const Icon(Icons.touch_app, size: 18, color: Colors.grey), + onTap: () { + // 点击列表项:先填入编辑框,然后直接保存退出 + controller.selectUrl(url); + controller.saveAndExit(); + }, + ), + ); + }, + ), + ), + ElevatedButton( + onPressed: controller.saveAndExit, + style: ElevatedButton.styleFrom( + minimumSize: const Size(double.infinity, 48), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text('保存配置', style: TextStyle(fontSize: 16)), + ), + ], + ), + ), + ); + } +} diff --git a/ln_jq_app/lib/storage_service.dart b/ln_jq_app/lib/storage_service.dart index 259ec81..60de5da 100644 --- a/ln_jq_app/lib/storage_service.dart +++ b/ln_jq_app/lib/storage_service.dart @@ -28,6 +28,8 @@ class StorageService extends GetxService { // 新增:用于标记“绑定车辆”弹窗是否已在本会话中显示过 static const String _bindDialogShownKey = 'bind_vehicle_dialog_shown'; + static const String _hostUrlKey = 'host_url'; + static StorageService get to => Get.find(); Future init() async { @@ -36,6 +38,13 @@ class StorageService extends GetxService { } // --- Getters --- + String? get hostUrl => _box.read(_hostUrlKey); + + ///:保存自定义域名 + Future saveHostUrl(String url) async { + await _box.write(_hostUrlKey, url); + } + bool get isLoggedIn => _box.read(_tokenKey)?.isNotEmpty ?? false; String? get token => _box.read(_tokenKey);