diff --git a/lib/core/consts/workorder_consts.dart b/lib/core/consts/workorder_consts.dart index c1d6b021..57af6e33 100644 --- a/lib/core/consts/workorder_consts.dart +++ b/lib/core/consts/workorder_consts.dart @@ -1,52 +1,3 @@ -import 'package:flutter/material.dart'; - -/// 工单模块颜色常量 -class WorkOrderColors { - WorkOrderColors._(); - - /// 主色调 - static const Color primary = Color(0xFF165DFF); - - /// 待处理色(橙色) - static const Color pending = Color(0xFFFF7D00); - - /// 执行中色(蓝色) - static const Color executing = Color(0xFF165DFF); - - /// 已完成色(绿色) - static const Color completed = Color(0xFF00B42A); - - /// 背景色 - static const Color background = Color(0xFFFFFFFF); - - /// 页面背景色 - static const Color pageBackground = Color(0xFFF7F8FA); - - /// 主要文字色 - static const Color primaryText = Color(0xFF1D2129); - - /// 次要文字色 - static const Color secondaryText = Color(0xFF4E5969); - - /// 辅助文字色 - static const Color auxiliaryText = Color(0xFF86909C); - - /// 分割线颜色 - static const Color divider = Color(0xFFF0F0F0); - - /// 进度条背景色 - static const Color progressBackground = Color(0xFFF0F0F0); - - /// 卡片阴影 - static List get cardShadow => [ - const BoxShadow( - color: Color(0x0D000000), - blurRadius: 4, - offset: Offset(0, 2), - ), - ]; -} - /// 工单模块尺寸常量 class WorkOrderDimensions { WorkOrderDimensions._(); @@ -71,21 +22,10 @@ class WorkOrderDimensions { class AppConstants { AppConstants._(); - static const Color primaryColor = WorkOrderColors.primary; - static const Color backgroundColor = WorkOrderColors.background; - static const Color primaryTextColor = WorkOrderColors.primaryText; - static const Color secondaryTextColor = WorkOrderColors.secondaryText; - static const Color auxiliaryTextColor = WorkOrderColors.auxiliaryText; - static const Color pendingColor = WorkOrderColors.pending; - static const Color executingColor = WorkOrderColors.executing; - static const Color completedColor = WorkOrderColors.completed; - static const double borderRadius = WorkOrderDimensions.borderRadius; static const double horizontalPadding = WorkOrderDimensions.horizontalPadding; static const double moduleSpacing = WorkOrderDimensions.moduleSpacing; static const double navigationBarHeight = WorkOrderDimensions.navigationBarHeight; - - static List get cardShadow => WorkOrderColors.cardShadow; } /// 工单状态枚举 diff --git a/lib/core/di/injection.dart b/lib/core/di/injection.dart index 9e6e7ed4..d68acc25 100644 --- a/lib/core/di/injection.dart +++ b/lib/core/di/injection.dart @@ -141,6 +141,7 @@ import '../../features/devices/domain/usecases/recovery_task_usecase.dart'; import '../../features/devices/presentation/bloc/device_task_cubit.dart'; import '../app/app_user_cubit.dart'; import '../localization/locale_cubit.dart'; +import '../theme/theme_cubit.dart'; import '../../features/home/presentation/bloc/permission_request_bloc.dart'; import '../network/dio_client.dart'; import '../network/net_message_dispatcher.dart'; @@ -216,7 +217,8 @@ Future init() async { ); /// 1.5 --- MQTT Data Sources --- - sl.registerFactory( + /// 改为 LazySingleton:列表页预启动订阅和详情页共享同一实例,stream 互通 + sl.registerLazySingleton( () => DroneOsdDataSourceImpl(sl(instanceName: 'droneOsdClient')), ); @@ -451,6 +453,9 @@ Future init() async { // 🔥 语言管理 Cubit (单例) sl.registerLazySingleton(() => LocaleCubit()); + // 🔥 主题管理 Cubit (单例,管理亮/暗/跟随系统) + sl.registerLazySingleton(() => ThemeCubit()); + // Tab 配置 Cubit (单例) sl.registerLazySingleton(() => TabConfigCubit(sl())); diff --git a/lib/core/network/mqtt/data/datasources/drone_osd_datasource.dart b/lib/core/network/mqtt/data/datasources/drone_osd_datasource.dart index 51d2079f..d995b8da 100644 --- a/lib/core/network/mqtt/data/datasources/drone_osd_datasource.dart +++ b/lib/core/network/mqtt/data/datasources/drone_osd_datasource.dart @@ -46,6 +46,7 @@ class DroneOsdDataSourceImpl implements DroneOsdDataSource { }) async { if (_isDisposed) return; + // 同一设备(SN 相同):只增加引用计数,共享订阅 if (_listenerCount > 0 && _deviceSn == deviceSn && _gatewaySn == gatewaySn && @@ -55,7 +56,11 @@ class DroneOsdDataSourceImpl implements DroneOsdDataSource { return; } + // 切换到不同设备:强制清理旧订阅 + // 不管引用计数多少,SN 变了就必须先取消旧订阅再订阅新的 if (_deviceSn != null || _gatewaySn != null) { + // 重置引用计数为 1,让 stopListening 能真正取消 MQTT 订阅 + _listenerCount = 1; await stopListening(); } @@ -113,15 +118,19 @@ class DroneOsdDataSourceImpl implements DroneOsdDataSource { if (_isDisposed) return; try { final jsonData = jsonDecode(message.payload) as Map; - final osdData = DroneOsdEntity.fromJson(jsonData); if (_gatewaySn != null && _gatewaySn!.isNotEmpty && message.topic.contains(_gatewaySn!)) { + // 注入来源 SN,供下游解析层验证消息归属 + jsonData['_sourceSn'] = _gatewaySn; + final osdData = DroneOsdEntity.fromJson(jsonData); _stationOsdController.add(osdData); } else if (_deviceSn != null && _deviceSn!.isNotEmpty && message.topic.contains(_deviceSn!)) { + jsonData['_sourceSn'] = _deviceSn; + final osdData = DroneOsdEntity.fromJson(jsonData); _droneOsdController.add(osdData); } } catch (e) { @@ -130,9 +139,28 @@ class DroneOsdDataSourceImpl implements DroneOsdDataSource { } void dispose() { + if (_isDisposed) return; _isDisposed = true; - stopListening(); + + // 强制清理:不受引用计数限制,确保 MQTT 订阅被真正取消 + // 避免切换机场时旧订阅残留导致数据串台 + _listenerCount = 0; + _subscription?.cancel(); + _subscription = null; + + if (_deviceSn != null && _deviceSn!.isNotEmpty) { + final deviceSn = _deviceSn!; + mqttClient.unsubscribe('thing/product/$deviceSn/osd'); + } + if (_gatewaySn != null && _gatewaySn!.isNotEmpty) { + final gatewaySn = _gatewaySn!; + mqttClient.unsubscribe('thing/product/$gatewaySn/osd'); + } + + _deviceSn = null; + _gatewaySn = null; + _droneOsdController.close(); _stationOsdController.close(); } -} \ No newline at end of file +} diff --git a/lib/core/theme/AppTheme.dart b/lib/core/theme/AppTheme.dart index ce4ad355..059f7845 100644 --- a/lib/core/theme/AppTheme.dart +++ b/lib/core/theme/AppTheme.dart @@ -2,12 +2,159 @@ import 'dart:io'; import 'package:flutter/material.dart'; +/// 应用级语义色扩展(浅色/深色各一套,随主题自动切换) +/// +/// 用法:`context.appColors.cardBackground` +/// 原理:颜色挂载在 ThemeData.extensions 上,主题切换时组件树自动重建, +/// 页面无需写 `isDark ? A : B` 判断 +@immutable +class AppColors extends ThemeExtension { + const AppColors({ + required this.pageBackground, + required this.cardBackground, + required this.fillBackground, + required this.textPrimary, + required this.textSecondary, + required this.textTertiary, + required this.divider, + required this.cardShadow, + required this.primary, + required this.danger, + required this.warning, + required this.success, + }); + + /// 页面背景色 + final Color pageBackground; + + /// 卡片背景色 + final Color cardBackground; + + /// 卡片内嵌块填充色(输入框、上传按钮等) + final Color fillBackground; + + /// 主要文字色 + final Color textPrimary; + + /// 次要文字色 + final Color textSecondary; + + /// 辅助文字色(占位、说明) + final Color textTertiary; + + /// 分割线/浅色边框 + final Color divider; + + /// 卡片阴影色 + final Color cardShadow; + + /// 品牌主色(两种模式下保持一致) + final Color primary; + + /// 危险/错误色(两种模式下保持一致) + final Color danger; + + /// 警告色(两种模式下保持一致) + final Color warning; + + /// 成功/完成色(两种模式下保持一致) + final Color success; + + /// 浅色语义色(取自设计稿) + static const AppColors light = AppColors( + pageBackground: Color(0xFFF5F7FA), + cardBackground: Color(0xFFFFFFFF), + fillBackground: Color(0xFFF8F9FA), + textPrimary: Color(0xFF1D2129), + textSecondary: Color(0xFF4E5969), + textTertiary: Color(0xFF86909C), + divider: Color(0xFFF0F0F0), + cardShadow: Color(0x0D000000), + primary: Color(0xFF165DFF), + danger: Color(0xFFF53F3F), + warning: Color(0xFFFF7D00), + success: Color(0xFF00B42A), + ); + + /// 深色语义色(与 AppTheme.darkTheme 的 surface 色保持一致) + static const AppColors dark = AppColors( + pageBackground: Color(0xFF1C1C1E), + cardBackground: Color(0xFF2C2C2E), + fillBackground: Color(0xFF3A3A3C), + textPrimary: Color(0xFFF2F3F5), + textSecondary: Color(0xFFC9CDD4), + textTertiary: Color(0xFF86909C), + divider: Color(0xFF3A3A3C), + cardShadow: Color(0x00000000), // 深色下无阴影,靠色差分层级 + primary: Color(0xFF165DFF), + danger: Color(0xFFF53F3F), + warning: Color(0xFFFF7D00), + success: Color(0xFF00B42A), + ); + + @override + AppColors copyWith({ + Color? pageBackground, + Color? cardBackground, + Color? fillBackground, + Color? textPrimary, + Color? textSecondary, + Color? textTertiary, + Color? divider, + Color? cardShadow, + Color? primary, + Color? danger, + Color? warning, + Color? success, + }) { + return AppColors( + pageBackground: pageBackground ?? this.pageBackground, + cardBackground: cardBackground ?? this.cardBackground, + fillBackground: fillBackground ?? this.fillBackground, + textPrimary: textPrimary ?? this.textPrimary, + textSecondary: textSecondary ?? this.textSecondary, + textTertiary: textTertiary ?? this.textTertiary, + divider: divider ?? this.divider, + cardShadow: cardShadow ?? this.cardShadow, + primary: primary ?? this.primary, + danger: danger ?? this.danger, + warning: warning ?? this.warning, + success: success ?? this.success, + ); + } + + @override + AppColors lerp(AppColors? other, double t) { + if (other == null) return this; + return AppColors( + pageBackground: Color.lerp(pageBackground, other.pageBackground, t)!, + cardBackground: Color.lerp(cardBackground, other.cardBackground, t)!, + fillBackground: Color.lerp(fillBackground, other.fillBackground, t)!, + textPrimary: Color.lerp(textPrimary, other.textPrimary, t)!, + textSecondary: Color.lerp(textSecondary, other.textSecondary, t)!, + textTertiary: Color.lerp(textTertiary, other.textTertiary, t)!, + divider: Color.lerp(divider, other.divider, t)!, + cardShadow: Color.lerp(cardShadow, other.cardShadow, t)!, + primary: Color.lerp(primary, other.primary, t)!, + danger: Color.lerp(danger, other.danger, t)!, + warning: Color.lerp(warning, other.warning, t)!, + success: Color.lerp(success, other.success, t)!, + ); + } +} + +/// 便捷访问扩展:`context.appColors` +extension AppColorsX on BuildContext { + AppColors get appColors => Theme.of(this).extension()!; +} + class AppTheme { // 基础色值定义 static const Color _pureWhite = Color(0xFFFFFFFF); static const Color _offWhite = Color(0xFFfafafc); // 稍微带点灰的白,用于背景增加层次感 static const Color _pureBlack = Color(0xFF000000); static const Color _darkGrey = Color(0xFF1C1C1E); // iOS 风格的深灰黑色 + static const Color _surfaceDark = Color(0xFF2C2C2E); // 深色模式下卡片、弹窗表面色 static ThemeData get lightTheme { return ThemeData( @@ -15,6 +162,9 @@ class AppTheme { brightness: Brightness.light, fontFamily: (Platform.isWindows) ? 'Microsoft YaHei' : null, + // 语义色扩展 + extensions: const [AppColors.light], + // 1. 整体背景色 scaffoldBackgroundColor: _offWhite, @@ -85,4 +235,84 @@ class AppTheme { ), ); } + + /// 深色主题 (与浅色主题对称的黑白简约风格) + static ThemeData get darkTheme { + return ThemeData( + useMaterial3: true, + brightness: Brightness.dark, + fontFamily: (Platform.isWindows) ? 'Microsoft YaHei' : null, + + // 语义色扩展 + extensions: const [AppColors.dark], + + // 1. 整体背景色 (iOS 风格深灰黑) + scaffoldBackgroundColor: _darkGrey, + + // 2. 核心颜色方案 (与浅色主题反色:主色白色) + colorScheme: const ColorScheme.dark( + primary: _pureWhite, // 主色设为白色 + onPrimary: _pureBlack, // 白色背景上的文字设为黑色 + surface: _surfaceDark, // 卡片、弹窗等表面颜色 + onSurface: _pureWhite, // 表面上的文字色 + ), + + // 3. 全局按钮主题 (白底黑字) + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: _pureWhite, + foregroundColor: _pureBlack, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), + ), + ), + + // 4. 填充按钮主题 (常用语 Material 3) + filledButtonTheme: FilledButtonThemeData( + style: FilledButton.styleFrom( + backgroundColor: _pureWhite, + foregroundColor: _pureBlack, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + + // 5. AppBar 主题 (深灰黑背景,白色文字) + appBarTheme: const AppBarTheme( + backgroundColor: _darkGrey, + surfaceTintColor: Colors.transparent, + elevation: 0, + centerTitle: true, + iconTheme: IconThemeData(color: _pureWhite), + titleTextStyle: TextStyle( + color: _pureWhite, + fontSize: 18, + fontWeight: FontWeight.w700, + letterSpacing: -0.5, + ), + ), + + // 6. 输入框主题 (深色填充,白色边框) + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: _surfaceDark, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: Colors.white12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: Colors.white12), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: _pureWhite, width: 1.5), + ), + ), + ); + } } diff --git a/lib/core/theme/theme_cubit.dart b/lib/core/theme/theme_cubit.dart new file mode 100644 index 00000000..63f1a920 --- /dev/null +++ b/lib/core/theme/theme_cubit.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +/// 全局主题管理 Cubit(仿 LocaleCubit 模式) +/// 支持 light / dark / system 三态,持久化到 SharedPreferences +class ThemeCubit extends Cubit { + static const String _themeKey = 'app_theme_mode'; + + ThemeCubit() : super(ThemeMode.light) { + _loadSavedThemeMode(); + } + + /// 从本地存储恢复上次保存的主题模式 + Future _loadSavedThemeMode() async { + try { + final prefs = await SharedPreferences.getInstance(); + final saved = prefs.getString(_themeKey); + emit(_parseThemeMode(saved)); + } catch (e) { + emit(ThemeMode.light); + } + } + + /// 设置主题模式并持久化 + Future setThemeMode(ThemeMode mode) async { + try { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString(_themeKey, mode.name); + emit(mode); + } catch (e) { + debugPrint('❌ [ThemeCubit] 保存主题设置失败: $e'); + } + } + + /// 夜间模式开关(Switch 二态切换,不涉及跟随系统) + Future toggleDarkMode(bool enabled) => + setThemeMode(enabled ? ThemeMode.dark : ThemeMode.light); + + /// 当前是否为深色模式 + bool get isDarkMode => state == ThemeMode.dark; + + /// 将持久化的字符串解析为 ThemeMode + ThemeMode _parseThemeMode(String? saved) { + switch (saved) { + case 'dark': + return ThemeMode.dark; + case 'system': + return ThemeMode.system; + default: + return ThemeMode.light; + } + } +} diff --git a/lib/features/auth/presentation/pages/login_page.dart b/lib/features/auth/presentation/pages/login_page.dart index 6385764d..1cfb1313 100644 --- a/lib/features/auth/presentation/pages/login_page.dart +++ b/lib/features/auth/presentation/pages/login_page.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart'; import 'package:maibu_satabot_v2/core/di/injection.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart'; import '../../../../core/router/route_paths.dart'; @@ -33,9 +34,9 @@ class _LoginPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, + backgroundColor: context.appColors.pageBackground, // 保持 AppBar 简洁或直接去掉 - appBar: AppBar(backgroundColor: Colors.white, elevation: 0), + appBar: AppBar(backgroundColor: context.appColors.pageBackground, elevation: 0), body: BlocListener( listener: (context, state) { debugPrint('🔍 [LOGIN] 登录状态变化: $state'); @@ -81,7 +82,7 @@ class _LoginPageState extends State { textAlign: TextAlign.center, style: TextStyle( fontSize: 10, - color: Colors.grey.shade600, + color: context.appColors.textTertiary, height: 1.3, ), ), @@ -91,12 +92,12 @@ class _LoginPageState extends State { ), const SizedBox(height: 16), - const Text( + Text( "账号登录v1.1.5", - style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Colors.brown), + style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: context.appColors.textPrimary), ), const SizedBox(height: 8), - const Text("请填写以下信息以验证身份", style: TextStyle(fontSize: 15, color: Colors.grey)), + Text("请填写以下信息以验证身份", style: TextStyle(fontSize: 15, color: context.appColors.textTertiary)), const SizedBox(height: 24), // 表单区域 @@ -104,7 +105,8 @@ class _LoginPageState extends State { _buildInputLabel("账号"), TextField( controller: _userCtrl, - cursorColor: Colors.black, + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), + cursorColor: context.appColors.textPrimary, decoration: _inputDecoration(hint: "请输入用户名"), ), @@ -115,11 +117,12 @@ class _LoginPageState extends State { TextField( controller: _pwdCtrl, obscureText: _obscurePwd, - cursorColor: Colors.black, + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), + cursorColor: context.appColors.textPrimary, decoration: _inputDecoration( hint: "请输入密码", suffixIcon: IconButton( - icon: Icon(_obscurePwd ? Icons.visibility_off_outlined : Icons.visibility_outlined, color: Colors.grey, size: 20), + icon: Icon(_obscurePwd ? Icons.visibility_off_outlined : Icons.visibility_outlined, color: context.appColors.textTertiary, size: 20), onPressed: () => setState(() => _obscurePwd = !_obscurePwd), ), ), @@ -165,9 +168,9 @@ class _LoginPageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( + Text( "没有账号吗?", - style: TextStyle(fontSize: 14, color: Colors.grey), + style: TextStyle(fontSize: 14, color: context.appColors.textTertiary), ), InkWell( onTap: () { @@ -182,7 +185,7 @@ class _LoginPageState extends State { "立即注册", style: TextStyle( fontSize: 14, - color: Colors.black, + color: context.appColors.textPrimary, fontWeight: FontWeight.bold, ), ), @@ -218,7 +221,7 @@ class _LoginPageState extends State { Widget _buildInputLabel(String label) { return Padding( padding: const EdgeInsets.only(bottom: 8.0), - child: Text(label, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600)), + child: Text(label, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: context.appColors.textSecondary)), ); } @@ -226,17 +229,17 @@ class _LoginPageState extends State { InputDecoration _inputDecoration({required String hint, Widget? suffixIcon}) { return InputDecoration( hintText: hint, - hintStyle: TextStyle(color: Colors.grey.shade400, fontSize: 14), + hintStyle: TextStyle(color: context.appColors.textTertiary, fontSize: 14), filled: true, - fillColor: Colors.grey.shade50, + fillColor: context.appColors.fillBackground, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: BorderSide(color: Colors.grey.shade200), + borderSide: BorderSide(color: context.appColors.divider), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: Colors.black, width: 1), + borderSide: BorderSide(color: context.appColors.primary, width: 1), ), suffixIcon: suffixIcon, ); @@ -252,7 +255,9 @@ class _LoginPageState extends State { width: 24, child: Checkbox( value: _isAgreed, - activeColor: Colors.black, + activeColor: context.appColors.primary, + checkColor: Colors.white, + side: BorderSide(color: context.appColors.divider), onChanged: (val) => setState(() => _isAgreed = val!), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), ), @@ -261,9 +266,9 @@ class _LoginPageState extends State { Expanded( child: Wrap( children: [ - const Text("我已阅读并同意 ", style: TextStyle(fontSize: 13, color: Colors.grey)), + Text("我已阅读并同意 ", style: TextStyle(fontSize: 13, color: context.appColors.textTertiary)), _protocolText("《用户协议》", () => _showProtocolDetail("用户协议")), - const Text(" 和 ", style: TextStyle(fontSize: 13, color: Colors.grey)), + Text(" 和 ", style: TextStyle(fontSize: 13, color: context.appColors.textTertiary)), _protocolText("《隐私政策》", () => _showProtocolDetail("隐私政策")), ], ), @@ -277,7 +282,7 @@ class _LoginPageState extends State { onTap: onTap, child: Text( text, - style: const TextStyle(fontSize: 13, color: Colors.black, fontWeight: FontWeight.bold), + style: TextStyle(fontSize: 13, color: context.appColors.textPrimary, fontWeight: FontWeight.bold), ), ); } @@ -286,18 +291,18 @@ class _LoginPageState extends State { showModalBottomSheet( context: context, isScrollControlled: true, - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))), builder: (context) => Container( padding: const EdgeInsets.all(24), height: MediaQuery.of(context).size.height * 0.7, child: Column( children: [ - Text(title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), - const Divider(height: 32), + Text(title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: context.appColors.textPrimary)), + Divider(height: 32, color: context.appColors.divider), Expanded( child: SingleChildScrollView( - child: Text("此处放置您的${title}详细内容...\n" * 20, style: const TextStyle(color: Colors.black87, height: 1.5)), + child: Text("此处放置您的${title}详细内容...\n" * 20, style: TextStyle(color: context.appColors.textSecondary, height: 1.5)), ), ), const SizedBox(height: 16), diff --git a/lib/features/auth/presentation/pages/register_page.dart b/lib/features/auth/presentation/pages/register_page.dart index b2307b45..1905b9d4 100644 --- a/lib/features/auth/presentation/pages/register_page.dart +++ b/lib/features/auth/presentation/pages/register_page.dart @@ -2,6 +2,7 @@ import 'package:cc_ui_kit/cc_ui_kit.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:maibu_satabot_v2/core/router/route_paths.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; class RegisterPage extends StatefulWidget { const RegisterPage({super.key}); @@ -29,12 +30,12 @@ class _RegisterPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, + backgroundColor: context.appColors.pageBackground, appBar: AppBar( - backgroundColor: Colors.white, + backgroundColor: context.appColors.pageBackground, elevation: 0, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.black), + icon: Icon(Icons.arrow_back, color: context.appColors.textPrimary), onPressed: () => context.go(RoutePaths.login), ), ), @@ -44,25 +45,26 @@ class _RegisterPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 40), - const Text( + Text( "账号注册", style: TextStyle( fontSize: 28, fontWeight: FontWeight.bold, - color: Colors.black, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), - const Text( + Text( "请填写以下信息完成注册", - style: TextStyle(fontSize: 15, color: Colors.grey), + style: TextStyle(fontSize: 15, color: context.appColors.textTertiary), ), const SizedBox(height: 40), _buildInputLabel("用户名"), TextField( controller: _userCtrl, - cursorColor: Colors.black, + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), + cursorColor: context.appColors.textPrimary, decoration: _inputDecoration(hint: "请设置用户名"), ), @@ -72,7 +74,8 @@ class _RegisterPageState extends State { TextField( controller: _pwdCtrl, obscureText: _obscurePwd, - cursorColor: Colors.black, + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), + cursorColor: context.appColors.textPrimary, decoration: _inputDecoration( hint: "请设置密码", suffixIcon: IconButton( @@ -80,7 +83,7 @@ class _RegisterPageState extends State { _obscurePwd ? Icons.visibility_off_outlined : Icons.visibility_outlined, - color: Colors.grey, + color: context.appColors.textTertiary, size: 20, ), onPressed: () => setState(() => _obscurePwd = !_obscurePwd), @@ -94,7 +97,8 @@ class _RegisterPageState extends State { TextField( controller: _confirmPwdCtrl, obscureText: _obscureConfirmPwd, - cursorColor: Colors.black, + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), + cursorColor: context.appColors.textPrimary, decoration: _inputDecoration( hint: "请再次输入密码", suffixIcon: IconButton( @@ -102,7 +106,7 @@ class _RegisterPageState extends State { _obscureConfirmPwd ? Icons.visibility_off_outlined : Icons.visibility_outlined, - color: Colors.grey, + color: context.appColors.textTertiary, size: 20, ), onPressed: () => setState( @@ -132,17 +136,17 @@ class _RegisterPageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( + Text( "已有账号?", - style: TextStyle(fontSize: 14, color: Colors.grey), + style: TextStyle(fontSize: 14, color: context.appColors.textTertiary), ), GestureDetector( onTap: () => context.go(RoutePaths.login), - child: const Text( + child: Text( "立即登录", style: TextStyle( fontSize: 14, - color: Colors.black, + color: context.appColors.textPrimary, fontWeight: FontWeight.bold, ), ), @@ -183,9 +187,10 @@ class _RegisterPageState extends State { padding: const EdgeInsets.only(bottom: 8.0), child: Text( label, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, + color: context.appColors.textSecondary, ), ), ); @@ -194,17 +199,17 @@ class _RegisterPageState extends State { InputDecoration _inputDecoration({required String hint, Widget? suffixIcon}) { return InputDecoration( hintText: hint, - hintStyle: TextStyle(color: Colors.grey.shade400, fontSize: 14), + hintStyle: TextStyle(color: context.appColors.textTertiary, fontSize: 14), filled: true, - fillColor: Colors.grey.shade50, + fillColor: context.appColors.fillBackground, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: BorderSide(color: Colors.grey.shade200), + borderSide: BorderSide(color: context.appColors.divider), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: Colors.black, width: 1), + borderSide: BorderSide(color: context.appColors.primary, width: 1), ), suffixIcon: suffixIcon, ); @@ -219,7 +224,9 @@ class _RegisterPageState extends State { width: 24, child: Checkbox( value: _isAgreed, - activeColor: Colors.black, + activeColor: context.appColors.primary, + checkColor: Colors.white, + side: BorderSide(color: context.appColors.divider), onChanged: (val) => setState(() => _isAgreed = val!), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), @@ -230,12 +237,12 @@ class _RegisterPageState extends State { Expanded( child: Wrap( children: [ - const Text( + Text( "我已阅读并同意 ", - style: TextStyle(fontSize: 13, color: Colors.grey), + style: TextStyle(fontSize: 13, color: context.appColors.textTertiary), ), _protocolText("《用户协议》", () => _showProtocolDetail("用户协议")), - const Text(" 和 ", style: TextStyle(fontSize: 13, color: Colors.grey)), + Text(" 和 ", style: TextStyle(fontSize: 13, color: context.appColors.textTertiary)), _protocolText("《隐私政策》", () => _showProtocolDetail("隐私政策")), ], ), @@ -249,9 +256,9 @@ class _RegisterPageState extends State { onTap: onTap, child: Text( text, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: Colors.black, + color: context.appColors.textPrimary, fontWeight: FontWeight.bold, ), ), @@ -262,7 +269,7 @@ class _RegisterPageState extends State { showModalBottomSheet( context: context, isScrollControlled: true, - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), @@ -273,14 +280,14 @@ class _RegisterPageState extends State { children: [ Text( title, - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: context.appColors.textPrimary), ), - const Divider(height: 32), + Divider(height: 32, color: context.appColors.divider), Expanded( child: SingleChildScrollView( child: Text( "此处放置您的${title}详细内容...\n" * 20, - style: const TextStyle(color: Colors.black87, height: 1.5), + style: TextStyle(color: context.appColors.textSecondary, height: 1.5), ), ), ), diff --git a/lib/features/devices/data/repositories/generate_path_repository_Impl.dart b/lib/features/devices/data/repositories/generate_path_repository_Impl.dart index be00eee1..ab4c723b 100644 --- a/lib/features/devices/data/repositories/generate_path_repository_Impl.dart +++ b/lib/features/devices/data/repositories/generate_path_repository_Impl.dart @@ -259,7 +259,8 @@ class PathRepositoryImpl implements PathRepository { return records; } - /// 鏍规嵁鍦虹珯ID鏌ヨ�宸ヤ綔璁板綍鍒楄〃锛圶ML鏍煎紡锛? + /// 鏍规嵁鍦虹珯ID鏌ヨ�宸ヤ綔璁板綍鍒楄 + /// 〃锛圶ML鏍煎紡锛? @override Future> getWorkRecordsBySiteId({ required int siteId, diff --git a/lib/features/main_container/presentation/main_wrapper.dart b/lib/features/main_container/presentation/main_wrapper.dart index 3e229759..9f21abfb 100644 --- a/lib/features/main_container/presentation/main_wrapper.dart +++ b/lib/features/main_container/presentation/main_wrapper.dart @@ -18,6 +18,7 @@ import 'package:maibu_satabot_v2/features/v2/report/presentation/pages/report_pa import 'package:maibu_satabot_v2/features/v2/device_list/presentation/float_bar/view/float_bar_widget.dart'; import 'package:maibu_satabot_v2/features/v2/device_list/presentation/float_bar/cubit/float_bar_setting_cubit.dart'; import 'package:maibu_satabot_v2/core/di/injection.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; class MainWrapper extends StatefulWidget { const MainWrapper({super.key}); @@ -205,7 +206,9 @@ class _MainWrapperState extends State { ], ), bottomNavigationBar: Container( - decoration: const BoxDecoration(color: Colors.white), + decoration: BoxDecoration( + color: context.appColors.cardBackground, + ), child: SafeArea( top: false, child: LayoutBuilder( @@ -217,15 +220,15 @@ class _MainWrapperState extends State { ), padding: const EdgeInsets.symmetric(vertical: 2), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(25), border: Border.all( - color: const Color(0xFFE5E6EB), + color: context.appColors.divider, width: 1, ), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 10, offset: const Offset(0, 2), ), @@ -244,10 +247,10 @@ class _MainWrapperState extends State { width: 44, height: 44, decoration: BoxDecoration( - color: const Color(0xFFF2F3F5), + color: context.appColors.divider, shape: BoxShape.circle, border: Border.all( - color: const Color(0xFFE5E6EB), + color: context.appColors.divider, width: 1, ), ), @@ -274,8 +277,8 @@ class _MainWrapperState extends State { _getIconData(item.icon), size: 24, color: isSelected - ? const Color(0xFF1D2129) - : const Color(0xFF86909C), + ? context.appColors.textPrimary + : context.appColors.textTertiary, ), const SizedBox(height: 4), Text( @@ -283,8 +286,8 @@ class _MainWrapperState extends State { style: TextStyle( fontSize: 11, color: isSelected - ? const Color(0xFF1D2129) - : const Color(0xFF86909C), + ? context.appColors.textPrimary + : context.appColors.textTertiary, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, diff --git a/lib/features/main_container/presentation/widgets/custom_bottom_nav_bar.dart b/lib/features/main_container/presentation/widgets/custom_bottom_nav_bar.dart index f236e2cb..b87e75fc 100644 --- a/lib/features/main_container/presentation/widgets/custom_bottom_nav_bar.dart +++ b/lib/features/main_container/presentation/widgets/custom_bottom_nav_bar.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/main_container/presentation/cubit/tab_config_cubit.dart'; class CustomBottomNavBar extends StatefulWidget { @@ -92,11 +93,11 @@ class _CustomBottomNavBarState extends State return Container( decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 10, offset: const Offset(0, -2), ), @@ -135,11 +136,11 @@ class _CustomBottomNavBarState extends State width: circleDiameter, height: circleDiameter, decoration: BoxDecoration( - color: const Color(0xFFF5F5F5), + color: context.appColors.divider, shape: BoxShape.circle, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.12), + color: context.appColors.cardShadow, blurRadius: 8, spreadRadius: 1, offset: Offset.zero, @@ -174,7 +175,9 @@ class _CustomBottomNavBarState extends State children: [ Icon( icon, - color: isSelected ? Colors.black : const Color(0xFF666666), + color: isSelected + ? context.appColors.textPrimary + : context.appColors.textSecondary, size: 24, ), const SizedBox(height: 4), @@ -182,7 +185,9 @@ class _CustomBottomNavBarState extends State label, style: TextStyle( fontSize: 12, - color: isSelected ? Colors.black : const Color(0xFF666666), + color: isSelected + ? context.appColors.textPrimary + : context.appColors.textSecondary, fontWeight: isSelected ? FontWeight.w500 : FontWeight.w400, ), ), diff --git a/lib/features/v2/device_list/presentation/pages/device_status_page.dart b/lib/features/v2/device_list/presentation/pages/device_status_page.dart index 5fc863fc..f59f3bab 100644 --- a/lib/features/v2/device_list/presentation/pages/device_status_page.dart +++ b/lib/features/v2/device_list/presentation/pages/device_status_page.dart @@ -28,6 +28,7 @@ import '../../domain/entities/drone_station_entity.dart'; // 🔥 添加 DroneSt import 'robot_list_page.dart'; import 'robot_control_page.dart'; import 'drone_station_detail_page.dart'; +import '../../../../../core/network/mqtt/data/datasources/drone_osd_datasource.dart'; import 'qr_scanner_page.dart'; import 'bind_device_page.dart'; import '../../../../remote_control/presentation/bloc/remote_control_cubit.dart'; @@ -756,6 +757,12 @@ class _DeviceStatusViewState extends State { return DroneStationItemCard( station: station, onTap: () { + // 预启动 OSD 订阅:进入详情页前就让 MQTT 数据流跑起来 + // 这样详情页一进来就能从 stream 读到数据,无需等待首帧推送 + sl().startListening( + deviceSn: station.deviceSn, + gatewaySn: station.gatewaySn, + ); Navigator.push( context, MaterialPageRoute( @@ -936,7 +943,8 @@ class _DeviceStatusViewState extends State { if (result != null && mounted) { Navigator.of(context).push( MaterialPageRoute( - builder: (_) => BindDevicePage(scanResult: result), + builder: (_) => + BindDevicePage(scanResult: result), ), ); } @@ -1129,6 +1137,11 @@ class _DeviceStatusViewState extends State { } if (matchedStation != null) { + // 预启动 OSD 订阅:进入详情页前就让 MQTT 数据流跑起来 + sl().startListening( + deviceSn: matchedStation.deviceSn, + gatewaySn: matchedStation.gatewaySn, + ); Navigator.push( context, MaterialPageRoute( diff --git a/lib/features/v2/device_list/presentation/pages/drone_station_detail_page.dart b/lib/features/v2/device_list/presentation/pages/drone_station_detail_page.dart index 18c32469..09c922e4 100644 --- a/lib/features/v2/device_list/presentation/pages/drone_station_detail_page.dart +++ b/lib/features/v2/device_list/presentation/pages/drone_station_detail_page.dart @@ -37,18 +37,22 @@ class _DroneStationDetailPageState extends State { late DroneOsdDataSource _osdDataSource; StreamSubscription? _stationOsdSubscription; - /// 静态缓存:跨页面实例保留上次的 host 数据 - static final Map _cachedHostData = {}; + /// 静态缓存:按 gatewaySn 分区,跨页面实例保留每个机场上次的 host 数据 + /// 避免进入新机场时显示上一个机场的缓存数据 + static final Map> _cachedHostDataBySn = {}; final ValueNotifier> _stationHostData = - ValueNotifier>( - // 初始化时从缓存加载,进入页面立即显示 - Map.from(_cachedHostData), - ); + ValueNotifier>({}); @override void initState() { super.initState(); + // 进入页面时加载当前机场的缓存数据(切回时立即显示) + final cached = _cachedHostDataBySn[widget.station.gatewaySn]; + if (cached != null && cached.isNotEmpty) { + _stationHostData.value = Map.from(cached); + } + _bloc = sl(); _bloc.add( UAVDetailLoad( @@ -64,8 +68,10 @@ class _DroneStationDetailPageState extends State { } void _startStationOsdListening() { + // 使用 station 自带的 deviceSn + gatewaySn,与列表页预订阅参数一致 + // 避免因参数不同导致 DataSource 重新订阅、打断数据流 _osdDataSource.startListening( - deviceSn: '', + deviceSn: widget.station.deviceSn, gatewaySn: widget.station.gatewaySn, ); @@ -76,18 +82,32 @@ class _DroneStationDetailPageState extends State { } void _parseAndUpdateHostData(DroneOsdEntity osd) { + // 防护:验证消息来源 SN 是否匹配当前机场,避免串台 + final sourceSn = osd.rawData['_sourceSn']; + if (sourceSn != null && sourceSn != widget.station.gatewaySn) { + debugPrint( + '⚠️ [DetailPage] 丢弃非当前机场 OSD: source=$sourceSn, current=${widget.station.gatewaySn}', + ); + return; + } + final data = osd.rawData; final hostData = data['data'] is Map ? (data['data'] as Map)['host'] : null; if (hostData == null || hostData is! Map) return; // 🔥 基于已有数据合并,只更新非 null 字段,避免 Type 2 消息覆盖已有值 - final Map merged = Map.from(_stationHostData.value); + final Map merged = Map.from( + _stationHostData.value, + ); void _put(String key, dynamic value) { if (value != null) merged[key] = value; } - _put('environment_temperature', (hostData['environment_temperature'] as num?)?.toDouble()); + _put( + 'environment_temperature', + (hostData['environment_temperature'] as num?)?.toDouble(), + ); _put('humidity', (hostData['humidity'] as num?)?.toDouble()); _put('wind_speed', (hostData['wind_speed'] as num?)?.toDouble()); _put('rainfall', hostData['rainfall']?.toString()); @@ -95,7 +115,10 @@ class _DroneStationDetailPageState extends State { _put('drone_in_dock', hostData['drone_in_dock']?.toString()); _put('temperature', (hostData['temperature'] as num?)?.toDouble()); _put('putter_state', hostData['putter_state']?.toString()); - _put('supplement_light_state', hostData['supplement_light_state']?.toString()); + _put( + 'supplement_light_state', + hostData['supplement_light_state']?.toString(), + ); _put('alarm_state', hostData['alarm_state']?.toString()); _put('emergency_stop_state', hostData['emergency_stop_state']?.toString()); _put('silent_mode', hostData['silent_mode']?.toString()); @@ -104,28 +127,42 @@ class _DroneStationDetailPageState extends State { _put('height', (hostData['height'] as num?)?.toDouble()); _put('latitude', (hostData['latitude'] as num?)?.toDouble()); _put('longitude', (hostData['longitude'] as num?)?.toDouble()); - _put('home_position_is_valid', hostData['home_position_is_valid']?.toString()); + _put( + 'home_position_is_valid', + hostData['home_position_is_valid']?.toString(), + ); _put('battery_store_mode', hostData['battery_store_mode']?.toString()); _put('first_power_on', hostData['first_power_on']?.toString()); _put('drone_charge_state', hostData['drone_charge_state']); - _put('air_conditioner', hostData['air_conditioner']); _put('network_state', hostData['network_state']); _put('position_state', hostData['position_state']); _put('storage', hostData['storage']); _put('sub_device', hostData['sub_device']); _put('alternate_land_point', hostData['alternate_land_point']); - if (hostData['air_conditioner'] is Map) { - final ac = hostData['air_conditioner'] as Map; - _put('air_conditioner_state', ac['air_conditioner_state']?.toString()); - _put('air_conditioner_switch_time', ac['switch_time']?.toString()); + // 🔥 优先处理空调信息(确保第一时间解析,避免与其他卡片显示不同步) + final acData = hostData['air_conditioner']; + if (acData is Map) { + _put('air_conditioner', acData); + + // 解析空调开关状态 + final acStateVal = int.tryParse(acData['air_conditioner_state']?.toString() ?? '') ?? -1; + if (acStateVal == 0 || acStateVal == 1) { + _put('air_conditioner_state', acStateVal.toString()); + } + + // 解析空调运行时间 + final switchTime = acData['switch_time']; + if (switchTime != null) { + _put('air_conditioner_switch_time', '$switchTime'); + } } _stationHostData.value = merged; - // 同步写入静态缓存 - _cachedHostData - ..clear() - ..addAll(merged); + // 同步写入按 gatewaySn 分区的静态缓存 + _cachedHostDataBySn[widget.station.gatewaySn] = Map.from( + merged, + ); } /// 🔥 页面重新激活时调用(从其他页面返回时) @@ -161,6 +198,9 @@ class _DroneStationDetailPageState extends State { @override void dispose() { _stationOsdSubscription?.cancel(); + // 只调用 stopListening 减少引用计数,不调用 dispose() + // 因为 DroneOsdDataSource 是 LazySingleton,被列表页和详情页共享 + // 详情页退出后列表页可能仍需要该订阅 _osdDataSource.stopListening(); _stationHostData.dispose(); _bloc.close(); @@ -291,11 +331,14 @@ class _DroneStationDetailPageState extends State { Widget _buildContent(UAVDetailEntity detail) { _detail = detail; - _droneSn = detail.deviceSn; + final newDroneSn = detail.deviceSn; - if (_droneSn != null && _droneSn!.isNotEmpty) { + // 只在 droneSn 真正变化时才重新订阅,避免 Bloc 刷新时重复 startListening + // 导致引用计数虚增、dispose 时无法真正取消订阅 + if (newDroneSn != null && newDroneSn.isNotEmpty && newDroneSn != _droneSn) { + _droneSn = newDroneSn; _osdDataSource.startListening( - deviceSn: _droneSn!, + deviceSn: newDroneSn, gatewaySn: widget.station.gatewaySn, ); } @@ -499,13 +542,18 @@ class _DroneStationDetailPageState extends State { String acStr = '未知'; Color acColor = const Color(0xFF86909C); if (acState != null) { - final state = int.tryParse(acState) ?? -1; - if (state == 1) { + // 🔥 增强兼容性:尝试多种数据格式(int、string等) + final acValue = acState is int ? acState : int.tryParse(acState.toString()) ?? -1; + if (acValue == 1) { acStr = '开启'; acColor = const Color(0xFF00B42A); - } else if (state == 0) { + } else if (acValue == 0) { acStr = '关闭'; acColor = const Color(0xFF86909C); + } else if (acValue == 2) { + // 部分设备使用 2 表示待机或其他状态 + acStr = '待机'; + acColor = const Color(0xFFFF7D00); } } diff --git a/lib/features/v2/device_list/presentation/widgets/device_bottom_nav_bar.dart b/lib/features/v2/device_list/presentation/widgets/device_bottom_nav_bar.dart index 27660e30..882e434d 100644 --- a/lib/features/v2/device_list/presentation/widgets/device_bottom_nav_bar.dart +++ b/lib/features/v2/device_list/presentation/widgets/device_bottom_nav_bar.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; /// 底部导航栏组件 class DeviceBottomNavBar extends StatelessWidget { @@ -15,9 +16,9 @@ class DeviceBottomNavBar extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, border: Border( - top: BorderSide(color: const Color(0xFFF2F3F5), width: 0.5), + top: BorderSide(color: context.appColors.divider, width: 0.5), ), ), child: SafeArea( @@ -26,30 +27,35 @@ class DeviceBottomNavBar extends StatelessWidget { child: Row( children: [ _buildNavItem( + context, icon: Icons.home_outlined, label: '首页', isSelected: currentIndex == 0, onTap: () => onTap(0), ), _buildNavItem( + context, icon: Icons.devices, label: '设备', isSelected: currentIndex == 1, onTap: () => onTap(1), ), _buildNavItem( + context, icon: Icons.notifications_outlined, label: '告警', isSelected: currentIndex == 2, onTap: () => onTap(2), ), _buildNavItem( + context, icon: Icons.assignment_outlined, label: '工单', isSelected: currentIndex == 3, onTap: () => onTap(3), ), _buildNavItem( + context, icon: Icons.person_outlined, label: '我的', isSelected: currentIndex == 4, @@ -62,7 +68,8 @@ class DeviceBottomNavBar extends StatelessWidget { ); } - Widget _buildNavItem({ + Widget _buildNavItem( + BuildContext context, { required IconData icon, required String label, required bool isSelected, @@ -78,7 +85,7 @@ class DeviceBottomNavBar extends StatelessWidget { icon, color: isSelected ? const Color(0xFF004CFD) - : const Color(0xFF86909C), + : context.appColors.textTertiary, size: 24, ), const SizedBox(height: 2), @@ -88,7 +95,7 @@ class DeviceBottomNavBar extends StatelessWidget { fontSize: 11, color: isSelected ? const Color(0xFF004FFF) - : const Color(0xFF86909C), + : context.appColors.textTertiary, fontWeight: isSelected ? FontWeight.w500 : FontWeight.normal, ), ), diff --git a/lib/features/v2/device_list/presentation/widgets/drone_osd_card.dart b/lib/features/v2/device_list/presentation/widgets/drone_osd_card.dart index 27a2bd2f..70c946c1 100644 --- a/lib/features/v2/device_list/presentation/widgets/drone_osd_card.dart +++ b/lib/features/v2/device_list/presentation/widgets/drone_osd_card.dart @@ -166,10 +166,29 @@ class _DroneOsdCardState extends State { oldWidget.gatewaySn != widget.gatewaySn || oldWidget.isDroneOnline != widget.isDroneOnline) { _subscription?.cancel(); + // 切换机场/无人机:重置状态,避免显示上一个机场的旧数据 + _resetStateForNewDevice(); _subscribeStream(); } } + /// 切换到新机场/无人机时重置所有状态 + void _resetStateForNewDevice() { + _hasReceivedData = false; + _isWaitingForPush = droneTaskStateManager.isWaitingForOsdPush.value; + _localWaitTimeoutTimer?.cancel(); + if (_isWaitingForPush) { + _startLocalWaitTimeout(); + } + // 重置所有字段为"未知" + for (final def in _fieldDefs) { + _valueNotifiers[def.key]?.value = '未知'; + _colorNotifiers[def.key]?.value = def.defaultColor; + } + // 触发一次重建以切换回离线/等待卡片 + if (mounted) setState(() {}); + } + @override void dispose() { droneTaskStateManager.isWaitingForOsdPush.removeListener(_onWaitingChanged); @@ -208,6 +227,12 @@ class _DroneOsdCardState extends State { } void _parseOsdFields(DroneOsdEntity osd) { + // 防护:验证消息来源 SN 是否匹配当前无人机,避免串台 + final sourceSn = osd.rawData['_sourceSn']; + if (sourceSn != null && sourceSn != widget.deviceSn) { + return; + } + final data = osd.rawData; final dataMap = data['data'] is Map ? data['data'] as Map : null; Map? droneData; diff --git a/lib/features/v2/device_list/presentation/widgets/drone_station_osd_card.dart b/lib/features/v2/device_list/presentation/widgets/drone_station_osd_card.dart index 6dc6cc2f..da7b4128 100644 --- a/lib/features/v2/device_list/presentation/widgets/drone_station_osd_card.dart +++ b/lib/features/v2/device_list/presentation/widgets/drone_station_osd_card.dart @@ -182,9 +182,9 @@ class _DroneStationOsdCardState extends State { final Map> _valueNotifiers = {}; final Map> _colorNotifiers = {}; - /// 静态缓存:跨页面实例保留上次收到的值,进入页面立即显示 - static final Map _cachedValues = {}; - static final Map _cachedColors = {}; + /// 静态缓存:按 gatewaySn 分区,每个机场独立缓存 + static final Map> _cachedValuesBySn = {}; + static final Map> _cachedColorsBySn = {}; /// 是否已收到过有效数据(用于首次标记) bool _hasReceivedData = false; @@ -197,17 +197,18 @@ class _DroneStationOsdCardState extends State { } void _initNotifiers() { + final sn = widget.gatewaySn; + final cachedVals = _cachedValuesBySn[sn] ?? {}; + final cachedCols = _cachedColorsBySn[sn] ?? {}; for (final def in _fieldDefs) { - // 优先用缓存值,没有缓存则显示'未知' - _valueNotifiers[def.key] = ValueNotifier( - _cachedValues[def.key] ?? '未知', - ); + // 优先用该机场的缓存值,没有缓存则显示'未知' + _valueNotifiers[def.key] = ValueNotifier(cachedVals[def.key] ?? '未知'); _colorNotifiers[def.key] = ValueNotifier( - _cachedColors[def.key] ?? def.defaultColor, + cachedCols[def.key] ?? def.defaultColor, ); } - // 如果有缓存数据,标记为已收到 - if (_cachedValues.isNotEmpty) { + // 如果该机场有缓存数据,标记为已收到 + if (cachedVals.isNotEmpty) { _hasReceivedData = true; } } @@ -218,10 +219,39 @@ class _DroneStationOsdCardState extends State { if (oldWidget.gatewaySn != widget.gatewaySn || oldWidget.isOnline != widget.isOnline) { _subscription?.cancel(); + // 切换机场:重置状态,立即清空旧机场数据 + _resetStateForNewDevice(); _subscribeStream(); } } + /// 切换到新机场时重置所有状态 + /// 优先从静态缓存加载该机场的历史数据(实现切回时立即显示) + /// 无缓存则重置为"未知",避免显示上一个机场的旧数据 + void _resetStateForNewDevice() { + final sn = widget.gatewaySn; + final cachedVals = _cachedValuesBySn[sn] ?? {}; + final cachedCols = _cachedColorsBySn[sn] ?? {}; + + _hasReceivedData = cachedVals.isNotEmpty; + + for (final def in _fieldDefs) { + final newVal = cachedVals[def.key] ?? '未知'; + final newCol = cachedCols[def.key] ?? def.defaultColor; + final vNotifier = _valueNotifiers[def.key]; + if (vNotifier != null && vNotifier.value != newVal) { + vNotifier.value = newVal; + } + final cNotifier = _colorNotifiers[def.key]; + if (cNotifier != null && cNotifier.value != newCol) { + cNotifier.value = newCol; + } + } + + // 触发一次重建以切换离线/数据卡片显示 + if (mounted) setState(() {}); + } + @override void dispose() { _subscription?.cancel(); @@ -246,10 +276,11 @@ class _DroneStationOsdCardState extends State { /// 更新单个字段的值和颜色(仅当值变化时才触发 ValueNotifier 重建) /// 同时写入静态缓存,下次进入页面可立即显示 void _updateField(String key, String value, [Color? color]) { - // 写入缓存 - _cachedValues[key] = value; + // 写入该机场的缓存 + final sn = widget.gatewaySn; + _cachedValuesBySn.putIfAbsent(sn, () => {})[key] = value; if (color != null) { - _cachedColors[key] = color; + _cachedColorsBySn.putIfAbsent(sn, () => {})[key] = color; } final vNotifier = _valueNotifiers[key]; if (vNotifier != null && vNotifier.value != value) { @@ -263,13 +294,19 @@ class _DroneStationOsdCardState extends State { } } - - void _parseOsdFields(DroneOsdEntity osd) { + // 防护:验证消息来源 SN 是否匹配当前机场,避免串台 + final sourceSn = osd.rawData['_sourceSn']; + if (sourceSn != null && sourceSn != widget.gatewaySn) { + return; + } + final data = osd.rawData; final hostData = data['data'] is Map ? (data['data'] as Map)['host'] : null; if (hostData == null || hostData is! Map) { - debugPrint('⚠️ [StationOsdCard] hostData 为空,rawData keys: ${data.keys.toList()}'); + debugPrint( + '⚠️ [StationOsdCard] hostData 为空,rawData keys: ${data.keys.toList()}', + ); return; } @@ -278,17 +315,35 @@ class _DroneStationOsdCardState extends State { // 🔥 调试日志:对比卡片期望的字段 vs 实际推送中存在的字段 final expectedKeys = [ - 'air_conditioner', 'cover_state', 'putter_state', 'supplement_light_state', - 'temperature', 'drone_in_dock', 'drone_charge_state', 'humidity', - 'wind_speed', 'rainfall', 'environment_temperature', 'alarm_state', - 'emergency_stop_state', 'silent_mode', 'mode_code', 'network_state', - 'position_state', 'storage', 'heading', 'height', 'sub_device', + 'air_conditioner', + 'cover_state', + 'putter_state', + 'supplement_light_state', + 'temperature', + 'drone_in_dock', + 'drone_charge_state', + 'humidity', + 'wind_speed', + 'rainfall', + 'environment_temperature', + 'alarm_state', + 'emergency_stop_state', + 'silent_mode', + 'mode_code', + 'network_state', + 'position_state', + 'storage', + 'heading', + 'height', + 'sub_device', 'battery_store_mode', ]; final hostMap = hostData as Map; final found = expectedKeys.where((k) => hostMap.containsKey(k)).toList(); final missing = expectedKeys.where((k) => !hostMap.containsKey(k)).toList(); - debugPrint('[StationOsdCard] host keys(${hostMap.length}): ${hostMap.keys.join(", ")}'); + debugPrint( + '[StationOsdCard] host keys(${hostMap.length}): ${hostMap.keys.join(", ")}', + ); debugPrint('[StationOsdCard] matched: ${found.join(", ")}'); if (missing.isNotEmpty) { debugPrint('[StationOsdCard] MISSING: ${missing.join(", ")}'); @@ -303,9 +358,7 @@ class _DroneStationOsdCardState extends State { _updateField( 'acState', acState == 1 ? '开启' : '关闭', - acState == 1 - ? const Color(0xFF00B42A) - : const Color(0xFF86909C), + acState == 1 ? const Color(0xFF00B42A) : const Color(0xFF86909C), ); } @@ -322,9 +375,7 @@ class _DroneStationOsdCardState extends State { _updateField( 'coverState', coverState == 1 ? '开启' : '关闭', - coverState == 1 - ? const Color(0xFFFF7D00) - : const Color(0xFF00B42A), + coverState == 1 ? const Color(0xFFFF7D00) : const Color(0xFF00B42A), ); } @@ -335,23 +386,19 @@ class _DroneStationOsdCardState extends State { _updateField( 'putterState', putterState == 1 ? '动作中' : '归位', - putterState == 1 - ? const Color(0xFFFF7D00) - : const Color(0xFF00B42A), + putterState == 1 ? const Color(0xFFFF7D00) : const Color(0xFF00B42A), ); } // 补光灯 final lightState = int.tryParse(hostData['supplement_light_state']?.toString() ?? '') ?? - -1; + -1; if (lightState == 0 || lightState == 1) { _updateField( 'lightState', lightState == 1 ? '开启' : '关闭', - lightState == 1 - ? const Color(0xFFFF7D00) - : const Color(0xFF86909C), + lightState == 1 ? const Color(0xFFFF7D00) : const Color(0xFF86909C), ); } @@ -376,9 +423,7 @@ class _DroneStationOsdCardState extends State { _updateField( 'droneDock', droneDock == 1 ? '在库内' : '出库', - droneDock == 1 - ? const Color(0xFF00B42A) - : const Color(0xFFFF7D00), + droneDock == 1 ? const Color(0xFF00B42A) : const Color(0xFFFF7D00), ); } @@ -391,9 +436,7 @@ class _DroneStationOsdCardState extends State { _updateField( 'chargeState', chargeState == 1 ? '充电中' : '未充电', - chargeState == 1 - ? const Color(0xFFFF7D00) - : const Color(0xFF86909C), + chargeState == 1 ? const Color(0xFFFF7D00) : const Color(0xFF86909C), ); } } @@ -423,21 +466,18 @@ class _DroneStationOsdCardState extends State { } // 降雨 - final rainfall = - int.tryParse(hostData['rainfall']?.toString() ?? '') ?? -1; + final rainfall = int.tryParse(hostData['rainfall']?.toString() ?? '') ?? -1; if (rainfall >= 0) { _updateField( 'rainfall', rainfall == 1 ? '降雨中' : '无降雨', - rainfall == 1 - ? const Color(0xFF165DFF) - : const Color(0xFF00B42A), + rainfall == 1 ? const Color(0xFF165DFF) : const Color(0xFF00B42A), ); } // 外部温度 - final externalTemp = - (hostData['environment_temperature'] as num?)?.toDouble(); + final externalTemp = (hostData['environment_temperature'] as num?) + ?.toDouble(); if (externalTemp != null) { _updateField( 'externalTemp', @@ -457,23 +497,18 @@ class _DroneStationOsdCardState extends State { _updateField( 'alarmState', alarmState != 0 ? '告警中' : '无告警', - alarmState != 0 - ? const Color(0xFFF53F3F) - : const Color(0xFF00B42A), + alarmState != 0 ? const Color(0xFFF53F3F) : const Color(0xFF00B42A), ); } // 急停状态 if (hostData.containsKey('emergency_stop_state')) { final emergencyStop = - int.tryParse(hostData['emergency_stop_state']?.toString() ?? '') ?? - 0; + int.tryParse(hostData['emergency_stop_state']?.toString() ?? '') ?? 0; _updateField( 'emergencyStop', emergencyStop == 1 ? '已触发' : '未触发', - emergencyStop == 1 - ? const Color(0xFFF53F3F) - : const Color(0xFF00B42A), + emergencyStop == 1 ? const Color(0xFFF53F3F) : const Color(0xFF00B42A), ); } @@ -484,9 +519,7 @@ class _DroneStationOsdCardState extends State { _updateField( 'silentMode', silentMode == 1 ? '开启' : '关闭', - silentMode == 1 - ? const Color(0xFFFF7D00) - : const Color(0xFF86909C), + silentMode == 1 ? const Color(0xFFFF7D00) : const Color(0xFF86909C), ); } @@ -577,7 +610,6 @@ class _DroneStationOsdCardState extends State { : const Color(0xFF86909C), ); } - } @override diff --git a/lib/features/v2/message_center/presentation/pages/message_center_page.dart b/lib/features/v2/message_center/presentation/pages/message_center_page.dart index 37ad9af8..71cf76d1 100644 --- a/lib/features/v2/message_center/presentation/pages/message_center_page.dart +++ b/lib/features/v2/message_center/presentation/pages/message_center_page.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:maibu_satabot_v2/core/di/injection.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/message_center/domain/entities/message_category_entity.dart'; import 'package:maibu_satabot_v2/features/v2/message_center/presentation/bloc/message_center_cubit.dart'; import 'package:maibu_satabot_v2/features/v2/message_center/presentation/bloc/message_center_state.dart'; -import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/constants/alarm_constants.dart'; /// 消息中心页面 class MessageCenterPage extends StatelessWidget { @@ -28,7 +28,7 @@ class MessageCenterView extends StatelessWidget { final cubit = context.read(); return Scaffold( - backgroundColor: AlarmColors.background, + backgroundColor: context.appColors.pageBackground, body: BlocConsumer( listener: (context, state) { if (state is MessageCenterActionInProgress) { @@ -70,11 +70,11 @@ class MessageCenterView extends StatelessWidget { // 右侧:一键已读按钮 GestureDetector( onTap: () => cubit.markAllAsRead(), - child: const Text( + child: Text( '一键已读', style: TextStyle( fontSize: 14, - color: AlarmColors.primary, + color: context.appColors.primary, fontWeight: FontWeight.w500, ), ), @@ -95,15 +95,15 @@ class MessageCenterView extends StatelessWidget { return DropdownButton( value: selectedType, underline: const SizedBox(), - icon: const Icon( + icon: Icon( Icons.keyboard_arrow_down, size: 20, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), items: MessageType.values.map((type) { return DropdownMenuItem( @@ -123,7 +123,7 @@ class MessageCenterView extends StatelessWidget { Widget _buildContent(BuildContext context, MessageCenterState state) { if (state is MessageCenterLoading) { - return _buildSkeletonScreen(); + return _buildSkeletonScreen(context); } if (state is MessageCenterError) { return _buildErrorState(context, state.message); @@ -142,9 +142,9 @@ class MessageCenterView extends StatelessWidget { return Center( child: Text( '暂无消息', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), ), ); @@ -176,7 +176,7 @@ class MessageCenterView extends StatelessWidget { } } - Widget _buildSkeletonScreen() { + Widget _buildSkeletonScreen(BuildContext context) { return ListView.separated( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), itemCount: 6, @@ -185,11 +185,11 @@ class MessageCenterView extends StatelessWidget { return Container( height: 80, decoration: BoxDecoration( - color: AlarmColors.background, - borderRadius: BorderRadius.circular(AlarmDimensions.borderRadius), + color: context.appColors.cardBackground, + borderRadius: BorderRadius.circular(16), ), - child: const Center( - child: CircularProgressIndicator(), + child: Center( + child: CircularProgressIndicator(color: context.appColors.primary), ), ); }, @@ -204,14 +204,14 @@ class MessageCenterView extends StatelessWidget { Icon( Icons.error_outline, size: 64, - color: AlarmColors.danger, + color: context.appColors.danger, ), const SizedBox(height: 16), Text( message, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), textAlign: TextAlign.center, ), @@ -244,19 +244,19 @@ class MessageCenterView extends StatelessWidget { child: Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ - const CircularProgressIndicator(), + CircularProgressIndicator(color: context.appColors.primary), const SizedBox(height: 16), Text( '$action中...', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), ), ], @@ -288,8 +288,8 @@ class _MessageCategoryCard extends StatelessWidget { child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AlarmColors.background, - borderRadius: BorderRadius.circular(AlarmDimensions.borderRadius), + color: context.appColors.cardBackground, + borderRadius: BorderRadius.circular(16), ), child: Row( children: [ @@ -307,16 +307,16 @@ class _MessageCategoryCard extends StatelessWidget { style: TextStyle( fontSize: 15, fontWeight: isUnread ? FontWeight.bold : FontWeight.w500, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 4), // 内容摘要 Text( message.content, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -330,13 +330,13 @@ class _MessageCategoryCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ // 未读角标 - if (isUnread) _buildUnreadBadge(), + if (isUnread) _buildUnreadBadge(context), // 时间 Text( message.time, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), ), ], @@ -391,13 +391,13 @@ class _MessageCategoryCard extends StatelessWidget { ); } - Widget _buildUnreadBadge() { + Widget _buildUnreadBadge(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: 4), child: Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( - color: AlarmColors.danger, + color: context.appColors.danger, borderRadius: BorderRadius.circular(10), ), child: Text( diff --git a/lib/features/v2/my/presentation/pages/my_page.dart b/lib/features/v2/my/presentation/pages/my_page.dart index a137fade..79072d0c 100644 --- a/lib/features/v2/my/presentation/pages/my_page.dart +++ b/lib/features/v2/my/presentation/pages/my_page.dart @@ -3,6 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/services.dart'; import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; +import 'package:maibu_satabot_v2/core/theme/theme_cubit.dart'; import 'package:maibu_satabot_v2/features/v2/my/presentation/cubit/my_cubit.dart'; import 'package:maibu_satabot_v2/features/v2/my/presentation/states/my_state.dart'; import 'package:maibu_satabot_v2/features/v2/my/di/my_di.dart'; @@ -38,7 +40,7 @@ class _MyPageContent extends StatelessWidget { ), child: Scaffold( extendBodyBehindAppBar: true, - backgroundColor: MyColors.background, + backgroundColor: context.appColors.pageBackground, body: BlocConsumer( listener: (context, state) { if (state is MyError) { @@ -59,6 +61,9 @@ class _MyPageContent extends StatelessWidget { final appUserState = context.watch().state; final user = appUserState.user; + // 🔥 监听全局主题状态,夜间模式开关的真实值以 ThemeCubit 为准 + final isDarkMode = context.watch().isDarkMode; + // 获取用户信息(优先使用昵称,其次用户名) final userName = user?.nickname ?? user?.username ?? '未知用户'; final userRole = user?.email ?? user?.phone ?? '未设置'; @@ -104,11 +109,11 @@ class _MyPageContent extends StatelessWidget { bottom: 16, ), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: 10, offset: const Offset(0, 2), ), @@ -123,11 +128,22 @@ class _MyPageContent extends StatelessWidget { itemCount: state.menuItems.length, itemBuilder: (context, index) { final item = state.menuItems[index]; + // 🔥 夜间模式开关值以全局 ThemeCubit 状态为准 + final displayItem = item.id == 'night_mode' + ? item.copyWith(switchValue: isDarkMode) + : item; return MenuItemCard( - item: item, + item: displayItem, onTap: () => _handleMenuItemTap(context, cubit, item), onSwitchChanged: item.hasSwitch - ? (value) => cubit.toggleNightMode(value) + ? (value) { + // 更新菜单 UI 状态 + cubit.toggleNightMode(value); + // 🔥 真正切换全局主题 + context + .read() + .toggleDarkMode(value); + } : null, ); }, diff --git a/lib/features/v2/my/presentation/pages/profile_detail_page.dart b/lib/features/v2/my/presentation/pages/profile_detail_page.dart index f0ea6610..14e9d9ec 100644 --- a/lib/features/v2/my/presentation/pages/profile_detail_page.dart +++ b/lib/features/v2/my/presentation/pages/profile_detail_page.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart'; import 'package:maibu_satabot_v2/core/app/app_user_state.dart'; import 'package:maibu_satabot_v2/core/domain/entities/user_entity.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; /// 个人信息详情页 class ProfileDetailPage extends StatelessWidget { @@ -11,12 +12,11 @@ class ProfileDetailPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: const Color(0xFFF5F7FA), + backgroundColor: context.appColors.pageBackground, appBar: AppBar( - backgroundColor: Colors.white, elevation: 0, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Color(0xFF1D2129)), + icon: Icon(Icons.arrow_back, color: context.appColors.textPrimary), onPressed: () => Navigator.pop(context), ), title: const Text( @@ -24,7 +24,6 @@ class ProfileDetailPage extends StatelessWidget { style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF1D2129), ), ), centerTitle: true, @@ -43,11 +42,11 @@ class ProfileDetailPage extends StatelessWidget { padding: const EdgeInsets.all(16), children: [ // 头像卡片 - _buildAvatarCard(user), + _buildAvatarCard(context, user), const SizedBox(height: 16), // 信息列表 - _buildInfoCard(user), + _buildInfoCard(context, user), const SizedBox(height: 16), // 操作按钮 @@ -60,15 +59,15 @@ class ProfileDetailPage extends StatelessWidget { } /// 头像卡片 - Widget _buildAvatarCard(UserEntity user) { + Widget _buildAvatarCard(BuildContext context, UserEntity user) { return Container( padding: const EdgeInsets.symmetric(vertical: 32), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: 8, offset: const Offset(0, 2), ), @@ -95,10 +94,10 @@ class ProfileDetailPage extends StatelessWidget { // 昵称 Text( user.nickname, - style: const TextStyle( + style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 8), @@ -107,7 +106,7 @@ class ProfileDetailPage extends StatelessWidget { '@${user.username}', style: TextStyle( fontSize: 14, - color: const Color(0xFF86909C), + color: context.appColors.textTertiary, ), ), ], @@ -142,14 +141,14 @@ class ProfileDetailPage extends StatelessWidget { } /// 信息卡片 - Widget _buildInfoCard(UserEntity user) { + Widget _buildInfoCard(BuildContext context, UserEntity user) { return Container( decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: 8, offset: const Offset(0, 2), ), @@ -158,18 +157,21 @@ class ProfileDetailPage extends StatelessWidget { child: Column( children: [ _buildInfoItem( + context, icon: Icons.person_outline, label: '用户ID', value: user.userId, showDivider: true, ), _buildInfoItem( + context, icon: Icons.account_circle_outlined, label: '用户名', value: user.username, showDivider: true, ), _buildInfoItem( + context, icon: Icons.badge_outlined, label: '昵称', value: user.nickname, @@ -177,6 +179,7 @@ class ProfileDetailPage extends StatelessWidget { ), if (user.email != null) _buildInfoItem( + context, icon: Icons.email_outlined, label: '邮箱', value: user.email!, @@ -184,6 +187,7 @@ class ProfileDetailPage extends StatelessWidget { ), if (user.phone != null) _buildInfoItem( + context, icon: Icons.phone_outlined, label: '手机号', value: user.phone!, @@ -195,7 +199,8 @@ class ProfileDetailPage extends StatelessWidget { } /// 信息项 - Widget _buildInfoItem({ + Widget _buildInfoItem( + BuildContext context, { required IconData icon, required String label, required String value, @@ -207,13 +212,13 @@ class ProfileDetailPage extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), child: Row( children: [ - Icon(icon, size: 22, color: const Color(0xFF86909C)), + Icon(icon, size: 22, color: context.appColors.textTertiary), const SizedBox(width: 12), Text( label, - style: const TextStyle( + style: TextStyle( fontSize: 15, - color: Color(0xFF86909C), + color: context.appColors.textTertiary, ), ), const Spacer(), @@ -221,9 +226,9 @@ class ProfileDetailPage extends StatelessWidget { flex: 2, child: Text( value, - style: const TextStyle( + style: TextStyle( fontSize: 15, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, fontWeight: FontWeight.w500, ), textAlign: TextAlign.right, @@ -237,7 +242,7 @@ class ProfileDetailPage extends StatelessWidget { Divider( height: 1, thickness: 1, - color: const Color(0xFFF2F3F5), + color: context.appColors.divider, ), ], ); diff --git a/lib/features/v2/my/presentation/pages/system_settings_page.dart b/lib/features/v2/my/presentation/pages/system_settings_page.dart index 6ffa8bd4..4a8983fb 100644 --- a/lib/features/v2/my/presentation/pages/system_settings_page.dart +++ b/lib/features/v2/my/presentation/pages/system_settings_page.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; import 'package:maibu_satabot_v2/core/localization/locale_cubit.dart'; import 'package:maibu_satabot_v2/core/router/route_paths.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/core/storage/user_storage.dart'; import 'package:maibu_satabot_v2/features/auth/presentation/bloc/auth_cubit.dart'; import 'package:maibu_satabot_v2/features/main_container/presentation/cubit/tab_config_cubit.dart'; @@ -22,20 +23,19 @@ class _SystemSettingsPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: const Color(0xFFF5F6F8), + backgroundColor: context.appColors.pageBackground, appBar: AppBar( - backgroundColor: Colors.white, elevation: 0, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Color(0xFF1D2129)), + icon: Icon(Icons.arrow_back, color: context.appColors.textPrimary), onPressed: () => Navigator.pop(context), ), title: Text( AppLocalizations.of(context).translate('my.settings'), - style: const TextStyle( + style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), centerTitle: true, @@ -65,11 +65,11 @@ class _SystemSettingsPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: const [ + boxShadow: [ BoxShadow( - color: Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: 8, offset: Offset(0, 2), ), @@ -83,10 +83,10 @@ class _SystemSettingsPageState extends State { children: [ Text( AppLocalizations.of(context).translate('my_v2.suspend_bar'), - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 4), @@ -94,9 +94,9 @@ class _SystemSettingsPageState extends State { AppLocalizations.of( context, ).translate('my_v2.suspend_bar_desc'), - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: Color(0xFF86909C), + color: context.appColors.textTertiary, ), ), ], @@ -124,11 +124,11 @@ class _SystemSettingsPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: const [ + boxShadow: [ BoxShadow( - color: Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: 8, offset: Offset(0, 2), ), @@ -139,10 +139,10 @@ class _SystemSettingsPageState extends State { children: [ Text( AppLocalizations.of(context).translate('my_v2.tab_settings'), - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 16), @@ -167,9 +167,9 @@ class _SystemSettingsPageState extends State { Expanded( child: Text( tab.name, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), ), @@ -197,11 +197,11 @@ class _SystemSettingsPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: const [ + boxShadow: [ BoxShadow( - color: Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: 8, offset: Offset(0, 2), ), @@ -212,10 +212,10 @@ class _SystemSettingsPageState extends State { children: [ Text( AppLocalizations.of(context).translate('my.language'), - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 16), @@ -267,7 +267,7 @@ class _SystemSettingsPageState extends State { border: Border.all( color: isSelected ? const Color(0xFF165DFF) - : const Color(0xFFE5E6EB), + : context.appColors.divider, width: 1, ), ), @@ -280,7 +280,7 @@ class _SystemSettingsPageState extends State { fontSize: 14, color: isSelected ? const Color(0xFF165DFF) - : const Color(0xFF1D2129), + : context.appColors.textPrimary, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), ), @@ -306,11 +306,11 @@ class _SystemSettingsPageState extends State { child: Container( padding: const EdgeInsets.symmetric(vertical: 16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: const [ + boxShadow: [ BoxShadow( - color: Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: 8, offset: Offset(0, 2), ), diff --git a/lib/features/v2/my/presentation/widgets/menu_item_card.dart b/lib/features/v2/my/presentation/widgets/menu_item_card.dart index 97f1e960..703cb3ab 100644 --- a/lib/features/v2/my/presentation/widgets/menu_item_card.dart +++ b/lib/features/v2/my/presentation/widgets/menu_item_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/my/domain/entities/my_entities.dart'; /// 菜单项卡片组件(100% 还原设计稿) @@ -32,17 +33,17 @@ class MenuItemCard extends StatelessWidget { Icon( _getIconData(item.icon), size: 20, - color: const Color(0xFF4E5969), + color: context.appColors.textSecondary, ), const SizedBox(width: 12), // 标题 Expanded( child: Text( _getLocalizedTitle(context, item), - style: const TextStyle( + style: TextStyle( fontSize: 15, fontWeight: FontWeight.w500, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -53,9 +54,9 @@ class MenuItemCard extends StatelessWidget { Flexible( child: Text( item.subTitle!, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: Color(0xFF86909C), + color: context.appColors.textTertiary, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -73,14 +74,14 @@ class MenuItemCard extends StatelessWidget { activeTrackColor: const Color( 0xFF165DFF, ).withOpacity(0.3), - inactiveThumbColor: const Color(0xFFC9CDD4), - inactiveTrackColor: const Color(0xFFE5E6EB), + inactiveThumbColor: context.appColors.textTertiary, + inactiveTrackColor: context.appColors.divider, ), ), ], Icon( Icons.chevron_right, - color: const Color(0xFFC9CDD4), + color: context.appColors.textTertiary, size: 20, ), ], @@ -94,7 +95,7 @@ class MenuItemCard extends StatelessWidget { child: Divider( height: 1, thickness: 0.5, - color: const Color(0xFFF0F0F0), + color: context.appColors.divider, ), ), ], diff --git a/lib/features/v2/my/presentation/widgets/quick_action_card.dart b/lib/features/v2/my/presentation/widgets/quick_action_card.dart index 9f12ed0d..03e574aa 100644 --- a/lib/features/v2/my/presentation/widgets/quick_action_card.dart +++ b/lib/features/v2/my/presentation/widgets/quick_action_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/my/presentation/constants/my_constants.dart'; /// 快捷入口卡片组件(我的消息、我的收藏、我的工单) @@ -21,11 +22,11 @@ class QuickActionCard extends StatelessWidget { margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 20), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: 10, offset: const Offset(0, 2), ), @@ -36,23 +37,26 @@ class QuickActionCard extends StatelessWidget { children: [ // 我的消息 _buildQuickActionItem( + context, icon: Icons.notifications_outlined, label: AppLocalizations.of(context).translate('my_v2.message'), onTap: onMessageTap, showBadge: true, ), // 分割线 - Container(width: 1, height: 40, color: const Color(0xFFF0F0F0)), + Container(width: 1, height: 40, color: context.appColors.divider), // 我的收藏 _buildQuickActionItem( + context, icon: Icons.favorite_border, label: AppLocalizations.of(context).translate('my_v2.favorite'), onTap: onFavoriteTap, ), // 分割线 - Container(width: 1, height: 40, color: const Color(0xFFF0F0F0)), + Container(width: 1, height: 40, color: context.appColors.divider), // 我的工单 _buildQuickActionItem( + context, icon: Icons.assignment_outlined, label: AppLocalizations.of(context).translate('my_v2.work_order'), onTap: onWorkOrderTap, @@ -62,7 +66,8 @@ class QuickActionCard extends StatelessWidget { ); } - Widget _buildQuickActionItem({ + Widget _buildQuickActionItem( + BuildContext context, { required IconData icon, required String label, required VoidCallback onTap, @@ -98,9 +103,9 @@ class QuickActionCard extends StatelessWidget { const SizedBox(height: 8), Text( label, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: MyColors.textSecondary, + color: context.appColors.textSecondary, ), maxLines: 2, overflow: TextOverflow.ellipsis, diff --git a/lib/features/v2/report/presentation/constants/report_constants.dart b/lib/features/v2/report/presentation/constants/report_constants.dart index 4c7b330a..4b46e881 100644 --- a/lib/features/v2/report/presentation/constants/report_constants.dart +++ b/lib/features/v2/report/presentation/constants/report_constants.dart @@ -1,18 +1,5 @@ import 'package:flutter/material.dart'; -/// 全局颜色常量 -class AppColors { - static const Color primary = Color(0xFF165DFF); - static const Color danger = Color(0xFFF53F3F); - static const Color warning = Color(0xFFFF7D00); - static const Color textPrimary = Color(0xFF1D2129); - static const Color textSecondary = Color(0xFF4E5969); - static const Color textHint = Color(0xFF86909C); - static const Color background = Color(0xFFFFFFFF); - static const Color cardBackground = Color(0xFFF8F9FA); - static const Color borderColor = Color(0xFFE5E6EB); -} - /// 全局尺寸常量 class AppDimensions { static const double borderRadius = 16.0; diff --git a/lib/features/v2/report/presentation/pages/report_page.dart b/lib/features/v2/report/presentation/pages/report_page.dart index 39ffe4df..829c2c06 100644 --- a/lib/features/v2/report/presentation/pages/report_page.dart +++ b/lib/features/v2/report/presentation/pages/report_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../cubit/report_cubit.dart'; import '../states/report_state.dart'; import '../constants/report_constants.dart'; @@ -30,10 +31,11 @@ class _ReportPageContent extends StatelessWidget { @override Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; return AnnotatedRegion( - value: SystemUiOverlayStyle.dark, + value: isDark ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark, child: Scaffold( - backgroundColor: AppColors.cardBackground, + backgroundColor: context.appColors.pageBackground, body: BlocConsumer( listener: (context, state) { if (state is ReportFormState) { @@ -60,8 +62,8 @@ class _ReportPageContent extends StatelessWidget { }, builder: (context, state) { if (state is ReportSubmitting || state is ReportDevicesLoading) { - return const Center( - child: CircularProgressIndicator(color: AppColors.primary), + return Center( + child: CircularProgressIndicator(color: context.appColors.primary), ); } @@ -90,7 +92,7 @@ class _ReportPageContent extends StatelessWidget { return PreferredSize( preferredSize: const Size.fromHeight(100), child: Container( - color: AppColors.background, + color: context.appColors.cardBackground, child: SafeArea( bottom: false, child: Padding( @@ -102,22 +104,22 @@ class _ReportPageContent extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( + Text( '现场上报', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, - color: AppColors.textPrimary, + color: context.appColors.textPrimary, ), ), GestureDetector( onTap: () { context.read().submitReport(); }, - child: const Text( + child: Text( '提交', style: TextStyle( - color: AppColors.primary, + color: context.appColors.primary, fontSize: 18, fontWeight: FontWeight.w600, ), @@ -132,9 +134,9 @@ class _ReportPageContent extends StatelessWidget { }, child: Row( children: [ - const Icon( + Icon( Icons.location_on, - color: AppColors.primary, + color: context.appColors.primary, size: 16, ), const SizedBox(width: 6), @@ -143,7 +145,7 @@ class _ReportPageContent extends StatelessWidget { state.location, style: TextStyle( fontSize: 14, - color: AppColors.textSecondary, + color: context.appColors.textSecondary, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -153,7 +155,7 @@ class _ReportPageContent extends StatelessWidget { Icon( Icons.arrow_drop_down, size: 20, - color: AppColors.textHint, + color: context.appColors.textTertiary, ), ], ), @@ -327,7 +329,7 @@ class _ReportPageContent extends StatelessWidget { showModalBottomSheet( context: context, - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), @@ -340,12 +342,12 @@ class _ReportPageContent extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - const Text( + Text( '选择场站', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 14), @@ -364,7 +366,7 @@ class _ReportPageContent extends StatelessWidget { ? Text('站点编码: ${site.siteCode}') : null, trailing: isSelected - ? const Icon(Icons.check, color: Color(0xFF165DFF)) + ? Icon(Icons.check, color: context.appColors.primary) : null, onTap: () { cubit.selectSite(site); @@ -462,12 +464,15 @@ class _ReportPageContent extends StatelessWidget { if (isRobotType) { if (newState.robots.isEmpty) { deviceList = [ - const Center( + Center( child: Padding( padding: EdgeInsets.all(32), child: Text( '暂无机器人数据', - style: TextStyle(fontSize: 14, color: Color(0xFF86909C)), + style: TextStyle( + fontSize: 14, + color: context.appColors.textTertiary, + ), ), ), ), @@ -492,12 +497,15 @@ class _ReportPageContent extends StatelessWidget { } else if (isDroneType) { if (newState.drones.isEmpty) { deviceList = [ - const Center( + Center( child: Padding( padding: EdgeInsets.all(32), child: Text( '暂无无人机数据', - style: TextStyle(fontSize: 14, color: Color(0xFF86909C)), + style: TextStyle( + fontSize: 14, + color: context.appColors.textTertiary, + ), ), ), ), @@ -520,7 +528,7 @@ class _ReportPageContent extends StatelessWidget { showModalBottomSheet( context: context, - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), @@ -535,10 +543,10 @@ class _ReportPageContent extends StatelessWidget { children: [ Text( isRobotType ? '选择机器人' : '选择无人机', - style: const TextStyle( + style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 14), diff --git a/lib/features/v2/report/presentation/widgets/description_input.dart b/lib/features/v2/report/presentation/widgets/description_input.dart index ac1775d0..309bc60c 100644 --- a/lib/features/v2/report/presentation/widgets/description_input.dart +++ b/lib/features/v2/report/presentation/widgets/description_input.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../constants/report_constants.dart'; /// 问题描述输入模块 @@ -22,11 +23,11 @@ class DescriptionInput extends StatelessWidget { ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.background, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(AppDimensions.borderRadius), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: AppDimensions.cardShadowBlur, offset: const Offset(0, 2), ), @@ -37,10 +38,10 @@ class DescriptionInput extends StatelessWidget { children: [ Text( loc.translate('report.problem_description'), - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), @@ -50,37 +51,37 @@ class DescriptionInput extends StatelessWidget { onChanged: onChanged, decoration: InputDecoration( hintText: loc.translate('report.description_hint'), - hintStyle: const TextStyle(color: AppColors.textHint), + hintStyle: TextStyle(color: context.appColors.textTertiary), border: OutlineInputBorder( borderRadius: BorderRadius.circular( AppDimensions.borderRadiusSmall, ), - borderSide: const BorderSide(color: AppColors.borderColor), + borderSide: BorderSide(color: context.appColors.divider), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular( AppDimensions.borderRadiusSmall, ), - borderSide: const BorderSide(color: AppColors.borderColor), + borderSide: BorderSide(color: context.appColors.divider), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular( AppDimensions.borderRadiusSmall, ), - borderSide: const BorderSide( - color: AppColors.primary, + borderSide: BorderSide( + color: context.appColors.primary, width: 2, ), ), counterText: '', ), - style: const TextStyle(fontSize: 14, color: AppColors.textPrimary), + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), ), Align( alignment: Alignment.centerRight, child: Text( '${description?.length ?? 0}/200', - style: const TextStyle(fontSize: 12, color: AppColors.textHint), + style: TextStyle(fontSize: 12, color: context.appColors.textTertiary), ), ), ], diff --git a/lib/features/v2/report/presentation/widgets/device_selector.dart b/lib/features/v2/report/presentation/widgets/device_selector.dart index da297c4b..8a39c07c 100644 --- a/lib/features/v2/report/presentation/widgets/device_selector.dart +++ b/lib/features/v2/report/presentation/widgets/device_selector.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../constants/report_constants.dart'; /// 设备选择器 @@ -19,11 +20,11 @@ class DeviceSelector extends StatelessWidget { ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.background, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(AppDimensions.borderRadius), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: AppDimensions.cardShadowBlur, offset: const Offset(0, 2), ), @@ -34,10 +35,10 @@ class DeviceSelector extends StatelessWidget { children: [ Text( AppLocalizations.of(context).translate('report.select_device'), - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + color: context.appColors.textPrimary, ), ), Expanded( @@ -53,8 +54,8 @@ class DeviceSelector extends StatelessWidget { style: TextStyle( fontSize: 12, color: selectedDevice != null - ? AppColors.textPrimary - : AppColors.textHint, + ? context.appColors.textPrimary + : context.appColors.textTertiary, ), maxLines: 2, overflow: TextOverflow.ellipsis, @@ -65,7 +66,7 @@ class DeviceSelector extends StatelessWidget { Icon( Icons.arrow_drop_down, size: 20, - color: AppColors.textHint, + color: context.appColors.textTertiary, ), ], ), diff --git a/lib/features/v2/report/presentation/widgets/level_selector.dart b/lib/features/v2/report/presentation/widgets/level_selector.dart index b674bd66..73ae3d38 100644 --- a/lib/features/v2/report/presentation/widgets/level_selector.dart +++ b/lib/features/v2/report/presentation/widgets/level_selector.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../constants/report_constants.dart'; class LevelSelector extends StatelessWidget { @@ -19,11 +20,11 @@ class LevelSelector extends StatelessWidget { ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.background, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(AppDimensions.borderRadius), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: AppDimensions.cardShadowBlur, offset: const Offset(0, 2), ), @@ -32,12 +33,12 @@ class LevelSelector extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '问题等级', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), @@ -53,14 +54,14 @@ class LevelSelector extends StatelessWidget { decoration: BoxDecoration( color: isSelected ? level.textColor.withOpacity(0.1) - : AppColors.background, + : context.appColors.cardBackground, borderRadius: BorderRadius.circular( AppDimensions.borderRadiusSmall, ), border: Border.all( color: isSelected ? level.borderColor - : AppColors.borderColor, + : context.appColors.divider, width: isSelected ? 2 : 1, ), ), diff --git a/lib/features/v2/report/presentation/widgets/media_uploader.dart b/lib/features/v2/report/presentation/widgets/media_uploader.dart index 43b1505c..bdfb737f 100644 --- a/lib/features/v2/report/presentation/widgets/media_uploader.dart +++ b/lib/features/v2/report/presentation/widgets/media_uploader.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../constants/report_constants.dart'; /// 媒体上传模块 @@ -31,11 +32,11 @@ class MediaUploader extends StatelessWidget { ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.background, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(AppDimensions.borderRadius), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: AppDimensions.cardShadowBlur, offset: const Offset(0, 2), ), @@ -46,10 +47,10 @@ class MediaUploader extends StatelessWidget { children: [ Text( loc.translate('report.upload_media'), - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), @@ -62,6 +63,7 @@ class MediaUploader extends StatelessWidget { children: [ // 拍照按钮 _buildUploadButton( + context, icon: Icons.camera_alt, label: loc.translate('report.take_photo'), onTap: onCameraTap, @@ -69,6 +71,7 @@ class MediaUploader extends StatelessWidget { const SizedBox(width: 8), // 录像按钮 _buildUploadButton( + context, icon: Icons.videocam, label: loc.translate('report.record_video'), onTap: onVideoTap, @@ -92,7 +95,7 @@ class MediaUploader extends StatelessWidget { borderRadius: BorderRadius.circular( AppDimensions.borderRadiusSmall, ), - color: AppColors.cardBackground, + color: context.appColors.fillBackground, ), child: ClipRRect( borderRadius: BorderRadius.circular( @@ -171,6 +174,7 @@ class MediaUploader extends StatelessWidget { }).toList(), // 添加按钮 _buildUploadButton( + context, icon: Icons.add, label: '', onTap: onAddFromGallery, @@ -185,7 +189,8 @@ class MediaUploader extends StatelessWidget { ); } - Widget _buildUploadButton({ + Widget _buildUploadButton( + BuildContext context, { required IconData icon, required String label, required VoidCallback onTap, @@ -197,10 +202,14 @@ class MediaUploader extends StatelessWidget { width: 80, height: 80, decoration: BoxDecoration( - color: isAddButton ? AppColors.cardBackground : AppColors.background, + color: isAddButton + ? context.appColors.fillBackground + : context.appColors.cardBackground, borderRadius: BorderRadius.circular(AppDimensions.borderRadiusMedium), border: Border.all( - color: isAddButton ? AppColors.borderColor : AppColors.primary, + color: isAddButton + ? context.appColors.divider + : context.appColors.primary, ), ), child: Column( @@ -208,7 +217,9 @@ class MediaUploader extends StatelessWidget { children: [ Icon( icon, - color: isAddButton ? AppColors.textHint : AppColors.primary, + color: isAddButton + ? context.appColors.textTertiary + : context.appColors.primary, size: 24, ), if (label.isNotEmpty) ...[ @@ -217,7 +228,9 @@ class MediaUploader extends StatelessWidget { label, style: TextStyle( fontSize: 12, - color: isAddButton ? AppColors.textHint : AppColors.primary, + color: isAddButton + ? context.appColors.textTertiary + : context.appColors.primary, ), ), ], diff --git a/lib/features/v2/report/presentation/widgets/report_type_selector.dart b/lib/features/v2/report/presentation/widgets/report_type_selector.dart index f25692d8..18584b20 100644 --- a/lib/features/v2/report/presentation/widgets/report_type_selector.dart +++ b/lib/features/v2/report/presentation/widgets/report_type_selector.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../constants/report_constants.dart'; class ReportTypeSelector extends StatelessWidget { @@ -33,20 +34,20 @@ class ReportTypeSelector extends StatelessWidget { ), decoration: BoxDecoration( color: isSelected - ? AppColors.primary.withOpacity(0.1) - : AppColors.background, + ? context.appColors.primary.withOpacity(0.1) + : context.appColors.cardBackground, borderRadius: BorderRadius.circular( AppDimensions.borderRadiusSmall, ), border: Border.all( color: isSelected - ? AppColors.primary - : AppColors.borderColor, + ? context.appColors.primary + : context.appColors.divider, width: isSelected ? 2 : 1, ), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: AppDimensions.cardShadowBlur, offset: const Offset(0, 2), ), @@ -58,8 +59,8 @@ class ReportTypeSelector extends StatelessWidget { Icon( type.icon, color: isSelected - ? AppColors.primary - : AppColors.textHint, + ? context.appColors.primary + : context.appColors.textTertiary, size: 24, ), const SizedBox(height: 6), @@ -69,8 +70,8 @@ class ReportTypeSelector extends StatelessWidget { fontSize: 12, height: 1.2, color: isSelected - ? AppColors.primary - : AppColors.textSecondary, + ? context.appColors.primary + : context.appColors.textSecondary, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, diff --git a/lib/features/v2/site/presentation/widgets/site_selector_widget.dart b/lib/features/v2/site/presentation/widgets/site_selector_widget.dart index 6b08d414..7160f859 100644 --- a/lib/features/v2/site/presentation/widgets/site_selector_widget.dart +++ b/lib/features/v2/site/presentation/widgets/site_selector_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/site/presentation/cubit/site_cubit.dart'; import 'package:maibu_satabot_v2/features/v2/home/domain/entities/site_entity.dart'; @@ -74,10 +75,10 @@ class _SiteSelectorWidgetState extends State { text, maxLines: 1, overflow: TextOverflow.ellipsis, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), ), @@ -86,8 +87,8 @@ class _SiteSelectorWidgetState extends State { Icons.keyboard_arrow_down, size: iconSize, color: widget.enabled - ? const Color(0xFF4E5969) - : const Color(0xFFC9CDD4), + ? context.appColors.textSecondary + : context.appColors.textTertiary, ), ], ); @@ -126,10 +127,10 @@ class _SiteSelectorWidgetState extends State { text, maxLines: 1, overflow: TextOverflow.ellipsis, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), ), @@ -138,8 +139,8 @@ class _SiteSelectorWidgetState extends State { Icons.keyboard_arrow_down, size: iconSize, color: widget.enabled - ? const Color(0xFF4E5969) - : const Color(0xFFC9CDD4), + ? context.appColors.textSecondary + : context.appColors.textTertiary, ), ], ); @@ -163,7 +164,7 @@ class _SiteSelectorWidgetState extends State { showModalBottomSheet( context: context, - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), @@ -182,21 +183,21 @@ class _SiteSelectorWidgetState extends State { child: Row( children: [ const Spacer(), - const Text( + Text( '选择场站', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), const Spacer(), GestureDetector( onTap: () => Navigator.pop(context), - child: const Icon( + child: Icon( Icons.close, size: 22, - color: Color(0xFF86909C), + color: context.appColors.textTertiary, ), ), ], @@ -217,7 +218,7 @@ class _SiteSelectorWidgetState extends State { margin: const EdgeInsets.symmetric(vertical: 2), decoration: BoxDecoration( color: isSelected - ? const Color(0xFFE8F3FF) + ? context.appColors.primary.withOpacity(0.1) : Colors.transparent, borderRadius: BorderRadius.circular(8), ), @@ -235,15 +236,15 @@ class _SiteSelectorWidgetState extends State { ? FontWeight.w600 : FontWeight.w400, color: isSelected - ? const Color(0xFF165DFF) - : const Color(0xFF1D2129), + ? context.appColors.primary + : context.appColors.textPrimary, ), ), trailing: isSelected - ? const Icon( + ? Icon( Icons.check, size: 20, - color: Color(0xFF165DFF), + color: context.appColors.primary, ) : null, onTap: () { diff --git a/lib/features/v2/waring_center/data/datasources/impl/alarm_remote_datasource_impl.dart b/lib/features/v2/waring_center/data/datasources/impl/alarm_remote_datasource_impl.dart index af8d15fa..744bb657 100644 --- a/lib/features/v2/waring_center/data/datasources/impl/alarm_remote_datasource_impl.dart +++ b/lib/features/v2/waring_center/data/datasources/impl/alarm_remote_datasource_impl.dart @@ -4,7 +4,6 @@ import 'package:dio/dio.dart'; import 'package:maibu_satabot_v2/core/consts/http_api_consts.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/data/datasources/alarm_remote_datasource.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/data/models/alarm_model.dart'; -import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/constants/alarm_constants.dart'; /// 告警远程数据源实现 class AlarmRemoteDataSourceImpl implements AlarmRemoteDataSource { diff --git a/lib/features/v2/waring_center/presentation/constants/alarm_constants.dart b/lib/features/v2/waring_center/presentation/constants/alarm_constants.dart index 056bc224..0ca80f55 100644 --- a/lib/features/v2/waring_center/presentation/constants/alarm_constants.dart +++ b/lib/features/v2/waring_center/presentation/constants/alarm_constants.dart @@ -4,7 +4,8 @@ library; import 'package:flutter/material.dart'; -/// 颜色常量 +/// 颜色常量(仅保留告警等级等模式无关的品牌语义色, +/// UI 层颜色请使用 context.appColors 语义色) class AlarmColors { /// 主色调 static const Color primary = Color(0xFF165DFF); @@ -20,24 +21,6 @@ class AlarmColors { /// 提示色 - 蓝色提示 static const Color info = Color(0xFF69b1ff); - - /// 背景色 - static const Color background = Color(0xFFFFFFFF); - - /// 主要文字色 - static const Color textPrimary = Color(0xFF1D2129); - - /// 次要文字色 - static const Color textSecondary = Color(0xFF4E5969); - - /// 辅助文字色 - static const Color textTertiary = Color(0xFF86909C); - - /// 未处理背景色 - static const Color unprocessedBg = Color(0xFFFFF0F0); - - /// AI诊断卡片背景色 - static const Color aiCardBg = Color(0xFFF0F7FF); } /// 尺寸常量 @@ -53,13 +36,6 @@ class AlarmDimensions { /// 导航栏高度 static const double appBarHeight = 44.0; - - /// 卡片阴影 - static BoxShadow cardShadow = const BoxShadow( - color: Color(0x0D000000), - blurRadius: 4, - offset: Offset(0, 2), - ); } /// 告警等级枚举 diff --git a/lib/features/v2/waring_center/presentation/cubit/alarm_dispatch_cubit.dart b/lib/features/v2/waring_center/presentation/cubit/alarm_dispatch_cubit.dart index e594b4ce..dcdbf539 100644 --- a/lib/features/v2/waring_center/presentation/cubit/alarm_dispatch_cubit.dart +++ b/lib/features/v2/waring_center/presentation/cubit/alarm_dispatch_cubit.dart @@ -5,9 +5,7 @@ import '../../domain/entities/alarm_detail_entity.dart'; import '../../domain/usecases/dispatch_workorder_usecase.dart'; import '../states/alarm_dispatch_state.dart'; import '../../../report/presentation/constants/report_constants.dart'; -import '../../../home/domain/entities/site_entity.dart'; import '../../../home/domain/usecases/get_site_list_usecase.dart'; -import '../../../home/domain/repositories/site_repository.dart'; import '../../../home/data/repositories/site_repository_impl.dart'; import '../../../home/data/datasources/site_datasource_impl.dart'; import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart'; diff --git a/lib/features/v2/waring_center/presentation/pages/alarm_center_page.dart b/lib/features/v2/waring_center/presentation/pages/alarm_center_page.dart index 132db772..ead5aa57 100644 --- a/lib/features/v2/waring_center/presentation/pages/alarm_center_page.dart +++ b/lib/features/v2/waring_center/presentation/pages/alarm_center_page.dart @@ -6,6 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/site/presentation/cubit/site_cubit.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/bloc/alarm_cubit.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/bloc/alarm_state.dart'; @@ -71,18 +72,22 @@ class _AlarmCenterPageState extends State { @override Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; return AnnotatedRegion( - value: SystemUiOverlayStyle.dark.copyWith( - statusBarColor: Colors.transparent, - statusBarIconBrightness: Brightness.dark, - ), + value: isDark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, child: Scaffold( - backgroundColor: const Color(0xFFF5F7FA), + backgroundColor: context.appColors.pageBackground, body: SafeArea( child: BlocBuilder( builder: (context, state) { if (state is AlarmLoading) { - return const Center(child: CircularProgressIndicator()); + return Center( + child: CircularProgressIndicator( + color: context.appColors.primary, + ), + ); } if (state is AlarmError) { @@ -90,10 +95,10 @@ class _AlarmCenterPageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( + Icon( Icons.error_outline, size: 48, - color: AlarmColors.textTertiary, + color: context.appColors.textTertiary, ), const SizedBox(height: 16), Text(state.message), @@ -192,7 +197,7 @@ class _AlarmCenterPageState extends State { void _showFilterDialog() { showModalBottomSheet( context: context, - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), @@ -207,10 +212,10 @@ class _AlarmCenterPageState extends State { AppLocalizations.of( context, ).translate('alarm_center.filter_title'), - style: const TextStyle( + style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 20), @@ -266,7 +271,7 @@ class _AlarmCenterPageState extends State { Navigator.pop(context); }, style: ElevatedButton.styleFrom( - backgroundColor: AlarmColors.primary, + backgroundColor: context.appColors.primary, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), @@ -302,14 +307,14 @@ class _AlarmCenterPageState extends State { children: [ const Flexible(child: SiteSelectorWidget(compact: true)), const SizedBox(width: 8), - Container(width: 1, height: 20, color: const Color(0xFFE5E6EB)), + Container(width: 1, height: 20, color: context.appColors.divider), const SizedBox(width: 8), Text( AppLocalizations.of(context).translate('alarm_center.title'), - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: Color(0xFF86909C), + color: context.appColors.textTertiary, ), ), ], @@ -319,10 +324,10 @@ class _AlarmCenterPageState extends State { Stack( children: [ IconButton( - icon: const Icon( + icon: Icon( Icons.notifications_none, size: 24, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), onPressed: () { context.push('/message_center'); @@ -335,8 +340,8 @@ class _AlarmCenterPageState extends State { child: Container( width: 8, height: 8, - decoration: const BoxDecoration( - color: Color(0xFFF53F3F), + decoration: BoxDecoration( + color: context.appColors.danger, shape: BoxShape.circle, ), ), @@ -345,10 +350,10 @@ class _AlarmCenterPageState extends State { ), // 筛选图标 IconButton( - icon: const Icon( + icon: Icon( Icons.filter_list, size: 24, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), onPressed: _showFilterDialog, ), @@ -360,7 +365,7 @@ class _AlarmCenterPageState extends State { Widget _buildTabBar(AlarmLoaded state) { return AlarmTabBar( selectedFilter: state.selectedFilter, - backgroundColor: const Color(0xFFF5F7FA), + backgroundColor: context.appColors.pageBackground, onFilterChanged: (filter) { _cubit.changeFilter(filter); }, @@ -369,16 +374,19 @@ class _AlarmCenterPageState extends State { Widget _buildLoadMoreIndicator(AlarmLoaded state) { if (state.isLoadingMore) { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 16), + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16), child: Center( child: Column( children: [ - CircularProgressIndicator(strokeWidth: 2), - SizedBox(height: 8), + CircularProgressIndicator( + strokeWidth: 2, + color: context.appColors.primary, + ), + const SizedBox(height: 8), Text( '加载中...', - style: TextStyle(fontSize: 12, color: AlarmColors.textTertiary), + style: TextStyle(fontSize: 12, color: context.appColors.textTertiary), ), ], ), @@ -387,23 +395,23 @@ class _AlarmCenterPageState extends State { } if (!state.hasMore) { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 16), + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16), child: Center( child: Text( '已加载全部数据', - style: TextStyle(fontSize: 12, color: AlarmColors.textTertiary), + style: TextStyle(fontSize: 12, color: context.appColors.textTertiary), ), ), ); } - return const Padding( - padding: EdgeInsets.symmetric(vertical: 16), + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16), child: Center( child: Text( '上滑加载更多', - style: TextStyle(fontSize: 12, color: AlarmColors.textTertiary), + style: TextStyle(fontSize: 12, color: context.appColors.textTertiary), ), ), ); @@ -415,10 +423,10 @@ class _AlarmCenterPageState extends State { children: [ Text( title, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 8), @@ -429,14 +437,14 @@ class _AlarmCenterPageState extends State { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( - border: Border.all(color: AlarmColors.textTertiary), + border: Border.all(color: context.appColors.divider), borderRadius: BorderRadius.circular(20), ), child: Text( option, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), ), ); diff --git a/lib/features/v2/waring_center/presentation/pages/alarm_detail_page.dart b/lib/features/v2/waring_center/presentation/pages/alarm_detail_page.dart index ada0ce4a..373506eb 100644 --- a/lib/features/v2/waring_center/presentation/pages/alarm_detail_page.dart +++ b/lib/features/v2/waring_center/presentation/pages/alarm_detail_page.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/domain/entities/alarm_detail_entity.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/bloc/alarm_detail_cubit.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/bloc/alarm_detail_state.dart'; -import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/constants/alarm_constants.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/widgets/metric_trend_chart.dart'; /// 告警详情页面 @@ -31,13 +31,13 @@ class _AlarmDetailPageState extends State { @override Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; return AnnotatedRegion( - value: SystemUiOverlayStyle.dark.copyWith( - statusBarColor: Colors.transparent, - statusBarIconBrightness: Brightness.dark, - ), + value: isDark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, child: Scaffold( - backgroundColor: const Color(0xFFF5F7FA), + backgroundColor: context.appColors.pageBackground, appBar: _buildAppBar(), body: BlocConsumer( listener: (context, state) { @@ -49,9 +49,9 @@ class _AlarmDetailPageState extends State { // 确认告警成功后显示提示 if (state is AlarmDetailLoaded && state.isConfirmed) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('告警已确认,正在处理中'), - backgroundColor: AlarmColors.success, + SnackBar( + content: const Text('告警已确认,正在处理中'), + backgroundColor: context.appColors.success, ), ); } @@ -91,26 +91,26 @@ class _AlarmDetailPageState extends State { PreferredSizeWidget _buildAppBar() { return AppBar( - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, elevation: 0, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: AlarmColors.textPrimary), + icon: Icon(Icons.arrow_back, color: context.appColors.textPrimary), onPressed: () => context.pop(), ), - title: const Text( + title: Text( '告警详情', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), ), centerTitle: true, actions: [ IconButton( - icon: const Icon( + icon: Icon( Icons.share_outlined, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), onPressed: () { // TODO: 分享功能 @@ -141,11 +141,11 @@ class _AlarmDetailPageState extends State { return Container( height: height, decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), ), child: Center( - child: CircularProgressIndicator(color: AlarmColors.primary), + child: CircularProgressIndicator(color: context.appColors.primary), ), ); } @@ -156,17 +156,17 @@ class _AlarmDetailPageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( + Icon( Icons.error_outline, size: 64, - color: AlarmColors.textTertiary, + color: context.appColors.textTertiary, ), const SizedBox(height: 16), Text( message, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), textAlign: TextAlign.center, ), @@ -176,7 +176,7 @@ class _AlarmDetailPageState extends State { icon: const Icon(Icons.refresh), label: const Text('重试'), style: ElevatedButton.styleFrom( - backgroundColor: AlarmColors.primary, + backgroundColor: context.appColors.primary, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12), ), ), @@ -191,18 +191,19 @@ class _AlarmDetailPageState extends State { color: Colors.black.withOpacity(0.3), child: Center( child: Card( + color: context.appColors.cardBackground, child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ - CircularProgressIndicator(color: AlarmColors.primary), + CircularProgressIndicator(color: context.appColors.primary), const SizedBox(height: 16), Text( '$action...', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), ), ], @@ -261,43 +262,45 @@ class _AlarmDetailPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: [AlarmDimensions.cardShadow], + boxShadow: [ + BoxShadow(color: context.appColors.cardShadow, blurRadius: 4, offset: const Offset(0, 2)), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - const Icon( + Icon( Icons.warning_amber_rounded, - color: AlarmColors.danger, + color: context.appColors.danger, size: 24, ), const SizedBox(width: 8), Expanded( child: Text( alarm.title, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( - color: AlarmColors.danger.withOpacity(0.1), + color: context.appColors.danger.withOpacity(0.1), borderRadius: BorderRadius.circular(4), ), child: Text( alarm.level.label, - style: const TextStyle( + style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, - color: AlarmColors.danger, + color: context.appColors.danger, ), ), ), @@ -306,17 +309,17 @@ class _AlarmDetailPageState extends State { const SizedBox(height: 12), Text( alarm.deviceInfo, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), ), const SizedBox(height: 4), Text( alarm.occurTime, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AlarmColors.textTertiary, + color: context.appColors.textTertiary, ), ), ], @@ -341,8 +344,8 @@ class _AlarmDetailPageState extends State { '告警状态', alarm.alarmStatus, valueColor: alarm.alarmStatus == '待处理' - ? AlarmColors.danger - : AlarmColors.success, + ? context.appColors.danger + : context.appColors.success, ); addRow('发生时间', alarm.occurTime); addRow('恢复时间', alarm.recoverTime); @@ -384,9 +387,11 @@ class _AlarmDetailPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: [AlarmDimensions.cardShadow], + boxShadow: [ + BoxShadow(color: context.appColors.cardShadow, blurRadius: 4, offset: const Offset(0, 2)), + ], ), child: Column( children: infoRows @@ -445,9 +450,9 @@ class _AlarmDetailPageState extends State { children: [ Text( label, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), ), const Spacer(), @@ -459,7 +464,7 @@ class _AlarmDetailPageState extends State { style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: valueColor ?? AlarmColors.textPrimary, + color: valueColor ?? context.appColors.textPrimary, ), ), ), @@ -469,7 +474,7 @@ class _AlarmDetailPageState extends State { } Widget _buildDivider() { - return const Divider(height: 24, thickness: 0.5, color: Color(0xFFE5E6EB)); + return Divider(height: 24, thickness: 0.5, color: context.appColors.divider); } bool _hasHistoryData(HistoryMetrics historyData) { @@ -483,19 +488,21 @@ class _AlarmDetailPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: [AlarmDimensions.cardShadow], + boxShadow: [ + BoxShadow(color: context.appColors.cardShadow, blurRadius: 4, offset: const Offset(0, 2)), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '处理建议', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), @@ -507,18 +514,18 @@ class _AlarmDetailPageState extends State { children: [ Text( '${entry.key + 1}. ', - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), ), Expanded( child: Text( entry.value, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, height: 1.5, ), ), @@ -542,10 +549,10 @@ class _AlarmDetailPageState extends State { return Container( padding: const EdgeInsets.fromLTRB(16, 12, 16, 20), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 8, offset: const Offset(0, -2), ), @@ -583,9 +590,9 @@ class _AlarmDetailPageState extends State { isDisabled: true, onPressed: () { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('暂未开放'), - backgroundColor: AlarmColors.warning, + SnackBar( + content: const Text('暂未开放'), + backgroundColor: context.appColors.warning, ), ); }, @@ -604,10 +611,10 @@ class _AlarmDetailPageState extends State { _cubit.loadAlarmDetail(widget.alarmId); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: AlarmColors.primary, + backgroundColor: context.appColors.cardBackground, + foregroundColor: context.appColors.primary, elevation: 0, - side: const BorderSide(color: AlarmColors.primary), + side: BorderSide(color: context.appColors.primary), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), @@ -639,10 +646,10 @@ class _AlarmDetailPageState extends State { child: ElevatedButton( onPressed: isLoading || isDisabled ? null : onPressed, style: ElevatedButton.styleFrom( - backgroundColor: isPrimary ? AlarmColors.primary : Colors.white, - foregroundColor: isPrimary ? Colors.white : AlarmColors.primary, + backgroundColor: isPrimary ? context.appColors.primary : context.appColors.cardBackground, + foregroundColor: isPrimary ? Colors.white : context.appColors.primary, elevation: 0, - side: isPrimary ? null : const BorderSide(color: AlarmColors.primary), + side: isPrimary ? null : BorderSide(color: context.appColors.primary), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), child: isLoading @@ -651,7 +658,7 @@ class _AlarmDetailPageState extends State { width: 20, child: CircularProgressIndicator( strokeWidth: 2, - color: isPrimary ? Colors.white : AlarmColors.primary, + color: isPrimary ? Colors.white : context.appColors.primary, ), ) : Text( @@ -671,18 +678,22 @@ class _AlarmDetailPageState extends State { context: context, builder: (context) { return AlertDialog( - title: const Text( + backgroundColor: context.appColors.cardBackground, + title: Text( 'AI诊断结果', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: context.appColors.textPrimary), ), content: SingleChildScrollView( child: Text( result, - style: const TextStyle(fontSize: 14, height: 1.6), + style: TextStyle(fontSize: 14, height: 1.6, color: context.appColors.textSecondary), ), ), actions: [ - TextButton(onPressed: () => context.pop(), child: const Text('关闭')), + TextButton( + onPressed: () => context.pop(), + child: Text('关闭', style: TextStyle(color: context.appColors.primary)), + ), ], ); }, diff --git a/lib/features/v2/waring_center/presentation/pages/alarm_dispatch_page.dart b/lib/features/v2/waring_center/presentation/pages/alarm_dispatch_page.dart index 9e19c32c..dfa51462 100644 --- a/lib/features/v2/waring_center/presentation/pages/alarm_dispatch_page.dart +++ b/lib/features/v2/waring_center/presentation/pages/alarm_dispatch_page.dart @@ -6,12 +6,12 @@ import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import '../../domain/entities/alarm_detail_entity.dart'; import '../../domain/usecases/dispatch_workorder_usecase.dart'; -import '../../data/datasources/alarm_dispatch_remote_datasource.dart'; import '../../data/datasources/impl/alarm_dispatch_remote_datasource_impl.dart'; import '../../data/repositories/alarm_dispatch_repository_impl.dart'; import '../cubit/alarm_dispatch_cubit.dart'; import '../states/alarm_dispatch_state.dart'; import '../../../report/presentation/constants/report_constants.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../../../report/presentation/widgets/report_type_selector.dart'; import '../../../report/presentation/widgets/device_selector.dart'; import '../../../report/presentation/widgets/description_input.dart'; @@ -58,7 +58,7 @@ class _AlarmDispatchPageContent extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: AppColors.cardBackground, + backgroundColor: context.appColors.pageBackground, appBar: _buildAppBar(context), body: BlocConsumer( listener: (context, state) { @@ -78,14 +78,14 @@ class _AlarmDispatchPageContent extends StatelessWidget { }, builder: (context, state) { if (state is AlarmDispatchSubmitting) { - return const Center( - child: CircularProgressIndicator(color: AppColors.primary), + return Center( + child: CircularProgressIndicator(color: context.appColors.primary), ); } if (state is AlarmDispatchLoading) { - return const Center( - child: CircularProgressIndicator(color: AppColors.primary), + return Center( + child: CircularProgressIndicator(color: context.appColors.primary), ); } @@ -101,28 +101,28 @@ class _AlarmDispatchPageContent extends StatelessWidget { PreferredSizeWidget _buildAppBar(BuildContext context) { return AppBar( - backgroundColor: AppColors.background, + backgroundColor: context.appColors.cardBackground, elevation: 0, - title: const Text( + title: Text( '告警派发工单', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: AppColors.textPrimary, + color: context.appColors.textPrimary, ), ), centerTitle: true, leading: IconButton( - icon: const Icon(Icons.arrow_back_ios, size: 20, color: Colors.black), + icon: Icon(Icons.arrow_back_ios, size: 20, color: context.appColors.textPrimary), onPressed: () => context.pop(), ), actions: [ TextButton( onPressed: () => context.read().submitDispatch(), - child: const Text( + child: Text( '提交', style: TextStyle( - color: AppColors.primary, + color: context.appColors.primary, fontSize: 16, fontWeight: FontWeight.w600, ), @@ -142,10 +142,10 @@ class _AlarmDispatchPageContent extends StatelessWidget { children: [ const SizedBox(height: AppDimensions.moduleSpacing), // 场站信息 - _buildSiteInfoCard(state), + _buildSiteInfoCard(context, state), const SizedBox(height: AppDimensions.moduleSpacing), // 告警关联信息条 - _buildAlarmInfoBar(state), + _buildAlarmInfoBar(context, state), const SizedBox(height: AppDimensions.moduleSpacing), // 上报类型选择 ReportTypeSelector( @@ -199,18 +199,18 @@ class _AlarmDispatchPageContent extends StatelessWidget { } /// 场站信息卡片 - Widget _buildSiteInfoCard(AlarmDispatchFormState state) { + Widget _buildSiteInfoCard(BuildContext context, AlarmDispatchFormState state) { return Container( margin: const EdgeInsets.symmetric( horizontal: AppDimensions.horizontalPadding, ), padding: const EdgeInsets.all(14), decoration: BoxDecoration( - color: AppColors.background, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(AppDimensions.borderRadius), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: AppDimensions.cardShadowBlur, offset: const Offset(0, 2), ), @@ -219,16 +219,16 @@ class _AlarmDispatchPageContent extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Row( + Row( children: [ - Icon(Icons.location_on, size: 18, color: AppColors.primary), - SizedBox(width: 6), + Icon(Icons.location_on, size: 18, color: context.appColors.primary), + const SizedBox(width: 6), Text( '场站信息', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + color: context.appColors.textPrimary, ), ), ], @@ -247,7 +247,7 @@ class _AlarmDispatchPageContent extends StatelessWidget { // ), // ), // const SizedBox(width: 24), - _buildInfoLabel('场站名称'), + _buildInfoLabel(context, '场站名称'), const SizedBox(width: 8), Expanded( child: Text( @@ -255,8 +255,8 @@ class _AlarmDispatchPageContent extends StatelessWidget { style: TextStyle( fontSize: 14, color: state.siteName != null - ? AppColors.textPrimary - : AppColors.textHint, + ? context.appColors.textPrimary + : context.appColors.textTertiary, fontWeight: FontWeight.w500, ), overflow: TextOverflow.ellipsis, @@ -267,14 +267,14 @@ class _AlarmDispatchPageContent extends StatelessWidget { const SizedBox(height: 8), Row( children: [ - _buildInfoLabel('告警编号'), + _buildInfoLabel(context, '告警编号'), const SizedBox(width: 8), Expanded( child: Text( state.alarmNo ?? state.alarmId, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.textPrimary, + color: context.appColors.textPrimary, fontWeight: FontWeight.w500, ), overflow: TextOverflow.ellipsis, @@ -287,45 +287,45 @@ class _AlarmDispatchPageContent extends StatelessWidget { ); } - Widget _buildInfoLabel(String label) { + Widget _buildInfoLabel(BuildContext context, String label) { return Text( label, - style: const TextStyle(fontSize: 12, color: AppColors.textHint), + style: TextStyle(fontSize: 12, color: context.appColors.textTertiary), ); } /// 告警关联信息条 - Widget _buildAlarmInfoBar(AlarmDispatchFormState state) { + Widget _buildAlarmInfoBar(BuildContext context, AlarmDispatchFormState state) { + final isDark = Theme.of(context).brightness == Brightness.dark; + final barColor = isDark ? const Color(0xFFE6B15A) : const Color(0xFFFA8C16); return Container( margin: const EdgeInsets.symmetric( horizontal: AppDimensions.horizontalPadding, ), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration( - color: const Color(0xFFFFF7E6), + color: isDark ? const Color(0xFF3A2E14) : const Color(0xFFFFF7E6), borderRadius: BorderRadius.circular(AppDimensions.borderRadiusSmall), - border: Border.all(color: const Color(0xFFFFD591)), + border: Border.all( + color: isDark ? const Color(0xFF6B5315) : const Color(0xFFFFD591), + ), ), child: Row( children: [ - const Icon( - Icons.warning_amber_rounded, - color: Color(0xFFFA8C16), - size: 18, - ), + Icon(Icons.warning_amber_rounded, color: barColor, size: 18), const SizedBox(width: 8), Expanded( child: Text( '关联告警: ${state.alarmNo ?? state.alarmId}', - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: Color(0xFF8C6E1F), + color: isDark ? const Color(0xFFE6C877) : const Color(0xFF8C6E1F), fontWeight: FontWeight.w500, ), overflow: TextOverflow.ellipsis, ), ), - const Icon(Icons.chevron_right, size: 18, color: Color(0xFFFA8C16)), + Icon(Icons.chevron_right, size: 18, color: barColor), ], ), ); @@ -343,11 +343,11 @@ class _AlarmDispatchPageContent extends StatelessWidget { ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.background, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(AppDimensions.borderRadius), boxShadow: [ BoxShadow( - color: const Color(0x0D000000), + color: context.appColors.cardShadow, blurRadius: AppDimensions.cardShadowBlur, offset: const Offset(0, 2), ), @@ -356,16 +356,17 @@ class _AlarmDispatchPageContent extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '计划时间', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), _buildTimeRow( + context, label: '计划开始', time: state.planStartTime, onTap: () => _pickDateTime(context, (time) { @@ -387,7 +388,8 @@ class _AlarmDispatchPageContent extends StatelessWidget { ); } - Widget _buildTimeRow({ + Widget _buildTimeRow( + BuildContext context, { required String label, required DateTime? time, required VoidCallback onTap, @@ -398,17 +400,17 @@ class _AlarmDispatchPageContent extends StatelessWidget { child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( - color: AppColors.cardBackground, + color: context.appColors.fillBackground, borderRadius: BorderRadius.circular(AppDimensions.borderRadiusSmall), - border: Border.all(color: AppColors.borderColor), + border: Border.all(color: context.appColors.divider), ), child: Row( children: [ Text( '$label${required ? ' *' : ''}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.textSecondary, + color: context.appColors.textSecondary, ), ), const Spacer(), @@ -420,15 +422,15 @@ class _AlarmDispatchPageContent extends StatelessWidget { style: TextStyle( fontSize: 14, color: time != null - ? AppColors.textPrimary - : AppColors.textHint, + ? context.appColors.textPrimary + : context.appColors.textTertiary, ), ), const SizedBox(width: 4), - const Icon( + Icon( Icons.calendar_today, size: 16, - color: AppColors.textHint, + color: context.appColors.textTertiary, ), ], ), diff --git a/lib/features/v2/waring_center/presentation/pages/alarm_handle_page.dart b/lib/features/v2/waring_center/presentation/pages/alarm_handle_page.dart index 5dbb6bee..454fcce8 100644 --- a/lib/features/v2/waring_center/presentation/pages/alarm_handle_page.dart +++ b/lib/features/v2/waring_center/presentation/pages/alarm_handle_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:maibu_satabot_v2/core/di/injection.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/domain/entities/alarm_detail_entity.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/bloc/alarm_detail_cubit.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/bloc/alarm_detail_state.dart'; @@ -54,7 +55,6 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { 'label': '确认告警', 'icon': Icons.check_circle_outline, 'color': const Color(0xFF52C41A), - 'bgColor': const Color(0xFFF6FFED), 'confirmedLabel': '已确认告警', 'confirmedTip': '确认无需', 'unconfirmedTip': '确认无需', @@ -63,35 +63,30 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { 'label': '派发工单', 'icon': Icons.file_copy_outlined, 'color': const Color(0xFF1890FF), - 'bgColor': const Color(0xFFE6F7FF), 'tip': '请填下方表格', }, { 'label': '联系负责人', 'icon': Icons.person_outline, 'color': const Color(0xFFFA8C16), - 'bgColor': const Color(0xFFFFF7E6), 'tip': '暂无功能', }, { 'label': '拍照取证', 'icon': Icons.camera_alt_outlined, 'color': const Color(0xFF722ED1), - 'bgColor': const Color(0xFFF9F0FF), 'tip': '暂不支持', }, { 'label': '备注记录', 'icon': Icons.edit_note_outlined, 'color': const Color(0xFF13C2C2), - 'bgColor': const Color(0xFFE6FFFB), 'tip': '请填下方表格', }, { 'label': '更多操作', 'icon': Icons.more_horiz, 'color': const Color(0xFF8C8C8C), - 'bgColor': const Color(0xFFF5F5F5), 'tip': '暂未开发', }, ]; @@ -99,16 +94,16 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: const Color(0xFFF5F5F5), + backgroundColor: context.appColors.pageBackground, appBar: _buildAppBar(), body: BlocConsumer( listener: (context, state) { if (state is AlarmDetailLoaded && state.isHandleSuccess) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('处理成功'), - backgroundColor: Color(0xFF52C41A), - duration: Duration(seconds: 1), + SnackBar( + content: const Text('处理成功'), + backgroundColor: context.appColors.success, + duration: const Duration(seconds: 1), ), ); Future.delayed(const Duration(seconds: 1), () { @@ -139,17 +134,17 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( + Icon( Icons.error_outline, size: 64, - color: Color(0xFFBFBFBF), + color: context.appColors.textTertiary, ), const SizedBox(height: 16), Text( state.message, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: Color(0xFF8C8C8C), + color: context.appColors.textTertiary, ), textAlign: TextAlign.center, ), @@ -161,7 +156,7 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { icon: const Icon(Icons.refresh), label: const Text('重试'), style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF1890FF), + backgroundColor: context.appColors.primary, padding: const EdgeInsets.symmetric( horizontal: 32, vertical: 12, @@ -173,7 +168,9 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { ); } - return const Center(child: CircularProgressIndicator()); + return Center( + child: CircularProgressIndicator(color: context.appColors.primary), + ); }, ), bottomNavigationBar: _buildBottomSubmitButton(), @@ -183,39 +180,39 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { AppBar _buildAppBar() { return AppBar( leading: IconButton( - icon: const Icon(Icons.arrow_back_ios, color: Colors.black, size: 20), + icon: Icon(Icons.arrow_back_ios, color: context.appColors.textPrimary, size: 20), onPressed: () => Navigator.pop(context), ), - title: const Text( + title: Text( '告警处理', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Colors.black, + color: context.appColors.textPrimary, ), ), - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, elevation: 0, ); } Widget _buildAlarmInfoCard(AlarmDetailEntity alarm) { final levelColor = alarm.level == AlarmLevel.danger - ? const Color(0xFFFF4D4F) + ? context.appColors.danger : alarm.level == AlarmLevel.warning - ? const Color(0xFFFA8C16) + ? context.appColors.warning : alarm.level == AlarmLevel.low - ? const Color(0xFF1890FF) - : const Color(0xFF52C41A); + ? context.appColors.primary + : context.appColors.success; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 4, offset: const Offset(0, 2), ), @@ -231,10 +228,10 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { Expanded( child: Text( alarm.title, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Colors.black, + color: context.appColors.textPrimary, ), overflow: TextOverflow.ellipsis, ), @@ -260,7 +257,7 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { const SizedBox(height: 8), Text( '${alarm.alarmNo ?? alarm.id} | ${alarm.deviceInfo} | ${alarm.occurTime}', - style: const TextStyle(fontSize: 13, color: Color(0xFF8C8C8C)), + style: TextStyle(fontSize: 13, color: context.appColors.textTertiary), ), ], ), @@ -271,11 +268,11 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 4, offset: const Offset(0, 2), ), @@ -319,7 +316,7 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { decoration: BoxDecoration( color: isSelected ? (button['color'] as Color) - : (button['bgColor'] as Color), + : (button['color'] as Color).withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: isSelected ? Border.all(color: button['color'] as Color, width: 2) @@ -391,11 +388,11 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 4, offset: const Offset(0, 2), ), @@ -425,22 +422,23 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { controller: _rootCauseController, maxLines: 4, maxLength: 200, - decoration: const InputDecoration( + decoration: InputDecoration( hintText: '请输入根因分析,最多200字', + hintStyle: TextStyle(color: context.appColors.textTertiary), border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 12), + contentPadding: const EdgeInsets.symmetric(vertical: 12), isDense: false, alignLabelWithHint: true, ), - style: const TextStyle(fontSize: 14, color: Colors.black), + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), ), Align( alignment: Alignment.bottomRight, child: Text( '${_rootCauseController.text.length}/200', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFFBFBFBF), + color: context.appColors.textTertiary, ), ), ), @@ -452,13 +450,14 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { label: '处理人', child: TextField( controller: _handlerController, - decoration: const InputDecoration( + decoration: InputDecoration( hintText: '请输入处理人', + hintStyle: TextStyle(color: context.appColors.textTertiary), border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 12), + contentPadding: const EdgeInsets.symmetric(vertical: 12), isDense: false, ), - style: const TextStyle(fontSize: 14, color: Colors.black), + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), ), ), _buildDivider(), @@ -471,22 +470,23 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { controller: _remarkController, maxLines: 4, maxLength: 200, - decoration: const InputDecoration( + decoration: InputDecoration( hintText: '请输入处理备注,最多200字', + hintStyle: TextStyle(color: context.appColors.textTertiary), border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 12), + contentPadding: const EdgeInsets.symmetric(vertical: 12), isDense: false, alignLabelWithHint: true, ), - style: const TextStyle(fontSize: 14, color: Colors.black), + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), ), Align( alignment: Alignment.bottomRight, child: Text( '${_remarkController.text.length}/200', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFFBFBFBF), + color: context.appColors.textTertiary, ), ), ), @@ -508,7 +508,7 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { width: 72, child: Text( label, - style: const TextStyle(fontSize: 14, color: Color(0xFF8C8C8C)), + style: TextStyle(fontSize: 14, color: context.appColors.textSecondary), ), ), const SizedBox(width: 12), @@ -533,7 +533,7 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { item.isEmpty ? hintText ?? '' : item, style: TextStyle( fontSize: 14, - color: item.isEmpty ? const Color(0xFFBFBFBF) : Colors.black, + color: item.isEmpty ? context.appColors.textTertiary : context.appColors.textPrimary, ), ), ); @@ -544,8 +544,8 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { contentPadding: EdgeInsets.symmetric(vertical: 12), isDense: false, ), - style: const TextStyle(fontSize: 14, color: Colors.black), - icon: const Icon(Icons.keyboard_arrow_down, color: Color(0xFFBFBFBF)), + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), + icon: Icon(Icons.keyboard_arrow_down, color: context.appColors.textTertiary), ); } @@ -553,11 +553,11 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { return DropdownButtonFormField( value: _handleResult.isEmpty ? null : _handleResult, items: [ - const DropdownMenuItem( + DropdownMenuItem( value: '', child: Text( '请选择处理结果', - style: TextStyle(fontSize: 14, color: Color(0xFFBFBFBF)), + style: TextStyle(fontSize: 14, color: context.appColors.textTertiary), ), ), ..._handleResultMap.entries.map((entry) { @@ -565,7 +565,7 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { value: entry.key, child: Text( entry.value, - style: const TextStyle(fontSize: 14, color: Colors.black), + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), ), ); }).toList(), @@ -578,23 +578,23 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { contentPadding: EdgeInsets.symmetric(vertical: 12), isDense: false, ), - style: const TextStyle(fontSize: 14, color: Colors.black), - icon: const Icon(Icons.keyboard_arrow_down, color: Color(0xFFBFBFBF)), + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), + icon: Icon(Icons.keyboard_arrow_down, color: context.appColors.textTertiary), ); } Widget _buildDivider() { - return const Divider(height: 1, color: Color(0xFFF0F0F0)); + return Divider(height: 1, color: context.appColors.divider); } Widget _buildBottomSubmitButton() { return Container( padding: const EdgeInsets.fromLTRB(16, 12, 16, 20), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 8, offset: const Offset(0, -2), ), @@ -605,7 +605,7 @@ class _AlarmHandlePageContentState extends State<_AlarmHandlePageContent> { child: ElevatedButton( onPressed: _submitHandle, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF1890FF), + backgroundColor: context.appColors.primary, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 14), diff --git a/lib/features/v2/waring_center/presentation/widgets/ai_diagnosis_card.dart b/lib/features/v2/waring_center/presentation/widgets/ai_diagnosis_card.dart index 16fe046e..b97d25ae 100644 --- a/lib/features/v2/waring_center/presentation/widgets/ai_diagnosis_card.dart +++ b/lib/features/v2/waring_center/presentation/widgets/ai_diagnosis_card.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/constants/alarm_constants.dart'; /// AI 诊断建议卡片 @@ -18,7 +19,7 @@ class AiDiagnosisCard extends StatelessWidget { margin: const EdgeInsets.all(AlarmDimensions.horizontalPadding), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AlarmColors.aiCardBg, + color: context.appColors.primary.withOpacity(0.1), borderRadius: BorderRadius.circular(AlarmDimensions.borderRadius), ), child: Column( @@ -29,8 +30,8 @@ class AiDiagnosisCard extends StatelessWidget { Container( width: 24, height: 24, - decoration: const BoxDecoration( - color: AlarmColors.primary, + decoration: BoxDecoration( + color: context.appColors.primary, shape: BoxShape.circle, ), child: const Icon( @@ -40,12 +41,12 @@ class AiDiagnosisCard extends StatelessWidget { ), ), const SizedBox(width: 8), - const Text( + Text( 'AI诊断建议', style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), ), ], @@ -53,9 +54,9 @@ class AiDiagnosisCard extends StatelessWidget { const SizedBox(height: 12), Text( content, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, height: 1.5, ), ), @@ -67,19 +68,19 @@ class AiDiagnosisCard extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ - const Text( + Text( '查看详情', style: TextStyle( fontSize: 13, - color: AlarmColors.primary, + color: context.appColors.primary, fontWeight: FontWeight.w500, ), ), const SizedBox(width: 4), - const Icon( + Icon( Icons.arrow_forward_ios, size: 12, - color: AlarmColors.primary, + color: context.appColors.primary, ), ], ), diff --git a/lib/features/v2/waring_center/presentation/widgets/alarm_count_card.dart b/lib/features/v2/waring_center/presentation/widgets/alarm_count_card.dart index 226a2298..78f72a93 100644 --- a/lib/features/v2/waring_center/presentation/widgets/alarm_count_card.dart +++ b/lib/features/v2/waring_center/presentation/widgets/alarm_count_card.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/domain/entities/alarm_count_entity.dart'; -import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/constants/alarm_constants.dart'; class AlarmCountCard extends StatelessWidget { const AlarmCountCard({ @@ -17,41 +17,46 @@ class AlarmCountCard extends StatelessWidget { Widget build(BuildContext context) { return Container( margin: const EdgeInsets.symmetric( - horizontal: AlarmDimensions.horizontalPadding, + horizontal: 16, ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AlarmColors.background, - borderRadius: BorderRadius.circular(AlarmDimensions.borderRadius), - boxShadow: [AlarmDimensions.cardShadow], + color: context.appColors.cardBackground, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow(color: context.appColors.cardShadow, blurRadius: 4, offset: const Offset(0, 2)), + ], ), child: Row( children: [ Expanded( child: _buildCountItem( + context, label: '待处理', value: count.unprocessed, - color: AlarmColors.danger, - bgColor: AlarmColors.unprocessedBg, - labelColor: AlarmColors.danger, + color: context.appColors.danger, + bgColor: context.appColors.danger.withOpacity(0.1), + labelColor: context.appColors.danger, ), ), - _buildDivider(), + _buildDivider(context), Expanded( child: _buildCountItem( + context, label: '已关闭', value: count.confirmed, - color: const Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), - _buildDivider(), + _buildDivider(context), Expanded( child: _buildCountItem( + context, label: AppLocalizations.of( context, ).translate('alarm_center.today_new'), value: count.todayNew, - color: const Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), ], @@ -59,7 +64,8 @@ class AlarmCountCard extends StatelessWidget { ); } - Widget _buildCountItem({ + Widget _buildCountItem( + BuildContext context, { required String label, required int value, required Color color, @@ -78,7 +84,7 @@ class AlarmCountCard extends StatelessWidget { label, style: TextStyle( fontSize: 13, - color: labelColor ?? AlarmColors.textSecondary, + color: labelColor ?? context.appColors.textSecondary, ), ), const SizedBox(height: 4), @@ -106,12 +112,12 @@ class AlarmCountCard extends StatelessWidget { ); } - Widget _buildDivider() { + Widget _buildDivider(BuildContext context) { return Container( width: 1, height: 50, margin: const EdgeInsets.symmetric(horizontal: 8), - color: const Color(0xFFE5E6EB), + color: context.appColors.divider, ); } } diff --git a/lib/features/v2/waring_center/presentation/widgets/alarm_item_card.dart b/lib/features/v2/waring_center/presentation/widgets/alarm_item_card.dart index e57e8a72..243c8b41 100644 --- a/lib/features/v2/waring_center/presentation/widgets/alarm_item_card.dart +++ b/lib/features/v2/waring_center/presentation/widgets/alarm_item_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/domain/entities/alarm_entity.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/constants/alarm_constants.dart'; @@ -21,9 +22,11 @@ class AlarmItemCard extends StatelessWidget { ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AlarmColors.background, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(AlarmDimensions.borderRadius), - boxShadow: [AlarmDimensions.cardShadow], + boxShadow: [ + BoxShadow(color: context.appColors.cardShadow, blurRadius: 4, offset: const Offset(0, 2)), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -36,10 +39,10 @@ class AlarmItemCard extends StatelessWidget { Expanded( child: Text( alarm.title, - style: const TextStyle( + style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), ), ), @@ -67,9 +70,9 @@ class AlarmItemCard extends StatelessWidget { // 第二行:区域和设备 Text( '${AppLocalizations.of(context).translate('alarm_center.area')}:${alarm.area} / ${AppLocalizations.of(context).translate('alarm_center.device')}:${alarm.device}', - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AlarmColors.textSecondary, + color: context.appColors.textSecondary, ), ), const SizedBox(height: 8), @@ -78,9 +81,9 @@ class AlarmItemCard extends StatelessWidget { children: [ Text( alarm.time, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AlarmColors.textTertiary, + color: context.appColors.textTertiary, ), ), const Spacer(), @@ -88,7 +91,7 @@ class AlarmItemCard extends StatelessWidget { alarm.status.label, style: TextStyle( fontSize: 12, - color: _getStatusColor(alarm.status), + color: _getStatusColor(context, alarm.status), fontWeight: FontWeight.w500, ), ), @@ -96,7 +99,7 @@ class AlarmItemCard extends StatelessWidget { Icon( Icons.arrow_forward_ios, size: 12, - color: _getStatusColor(alarm.status), + color: _getStatusColor(context, alarm.status), ), ], ), @@ -121,14 +124,14 @@ class AlarmItemCard extends StatelessWidget { } } - Color _getStatusColor(AlarmStatus status) { + Color _getStatusColor(BuildContext context, AlarmStatus status) { switch (status) { case AlarmStatus.pending: - return AlarmColors.danger; + return context.appColors.danger; case AlarmStatus.processing: case AlarmStatus.closed: case AlarmStatus.ignored: - return AlarmColors.textTertiary; + return context.appColors.textTertiary; } } } diff --git a/lib/features/v2/waring_center/presentation/widgets/alarm_tab_bar.dart b/lib/features/v2/waring_center/presentation/widgets/alarm_tab_bar.dart index a8793f0b..c0f3c099 100644 --- a/lib/features/v2/waring_center/presentation/widgets/alarm_tab_bar.dart +++ b/lib/features/v2/waring_center/presentation/widgets/alarm_tab_bar.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/constants/alarm_constants.dart'; /// 告警筛选标签栏 - 全部/未处理/已确认/已恢复 @@ -77,8 +78,8 @@ class AlarmTabBar extends StatelessWidget { fontSize: 15, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, color: isSelected - ? AlarmColors.primary - : AlarmColors.textSecondary, + ? context.appColors.primary + : context.appColors.textSecondary, ), ), const SizedBox(height: 4), @@ -86,7 +87,7 @@ class AlarmTabBar extends StatelessWidget { height: 2, width: 20, decoration: BoxDecoration( - color: isSelected ? AlarmColors.primary : Colors.transparent, + color: isSelected ? context.appColors.primary : Colors.transparent, borderRadius: BorderRadius.circular(1), ), ), diff --git a/lib/features/v2/waring_center/presentation/widgets/metric_trend_chart.dart b/lib/features/v2/waring_center/presentation/widgets/metric_trend_chart.dart index f965cad1..2063d8cb 100644 --- a/lib/features/v2/waring_center/presentation/widgets/metric_trend_chart.dart +++ b/lib/features/v2/waring_center/presentation/widgets/metric_trend_chart.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/domain/entities/alarm_detail_entity.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/bloc/alarm_detail_state.dart'; -import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/constants/alarm_constants.dart'; /// 指标趋势图组件 class MetricTrendChart extends StatelessWidget { @@ -21,37 +21,39 @@ class MetricTrendChart extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: [AlarmDimensions.cardShadow], + boxShadow: [ + BoxShadow(color: context.appColors.cardShadow, blurRadius: 4, offset: const Offset(0, 2)), + ], ), padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '指标趋势', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: AlarmColors.textPrimary, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), // 指标切换标签 - _buildMetricTabs(), + _buildMetricTabs(context), const SizedBox(height: 16), // 折线图 SizedBox( height: 200, - child: _buildChart(), + child: _buildChart(context), ), ], ), ); } - Widget _buildMetricTabs() { + Widget _buildMetricTabs(BuildContext context) { return Row( children: MetricType.values.map((metric) { final isSelected = metric == selectedMetric; @@ -62,7 +64,7 @@ class MetricTrendChart extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 8), margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( - color: isSelected ? const Color(0xFFE8F3FF) : Colors.transparent, + color: isSelected ? context.appColors.primary.withOpacity(0.1) : Colors.transparent, borderRadius: BorderRadius.circular(6), ), child: Text( @@ -71,7 +73,7 @@ class MetricTrendChart extends StatelessWidget { style: TextStyle( fontSize: 13, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, - color: isSelected ? AlarmColors.primary : AlarmColors.textSecondary, + color: isSelected ? context.appColors.primary : context.appColors.textSecondary, ), ), ), @@ -81,7 +83,7 @@ class MetricTrendChart extends StatelessWidget { ); } - Widget _buildChart() { + Widget _buildChart(BuildContext context) { List dataPoints; double maxY; @@ -101,10 +103,10 @@ class MetricTrendChart extends StatelessWidget { } if (dataPoints.isEmpty) { - return const Center( + return Center( child: Text( '暂无数据', - style: TextStyle(color: AlarmColors.textTertiary), + style: TextStyle(color: context.appColors.textTertiary), ), ); } @@ -123,13 +125,13 @@ class MetricTrendChart extends StatelessWidget { horizontalInterval: maxY / 5, getDrawingHorizontalLine: (value) { return FlLine( - color: const Color(0xFFE5E6EB).withOpacity(0.3), + color: context.appColors.divider.withOpacity(0.3), strokeWidth: 1, ); }, getDrawingVerticalLine: (value) { return FlLine( - color: const Color(0xFFE5E6EB).withOpacity(0.3), + color: context.appColors.divider.withOpacity(0.3), strokeWidth: 1, ); }, @@ -146,9 +148,9 @@ class MetricTrendChart extends StatelessWidget { if (index == 0 || index == dataPoints.length ~/ 2 || index == dataPoints.length - 1) { return Text( dataPoints[index].time, - style: const TextStyle( + style: TextStyle( fontSize: 11, - color: AlarmColors.textTertiary, + color: context.appColors.textTertiary, ), ); } @@ -165,9 +167,9 @@ class MetricTrendChart extends StatelessWidget { getTitlesWidget: (value, meta) { return Text( value.toInt().toString(), - style: const TextStyle( + style: TextStyle( fontSize: 11, - color: AlarmColors.textTertiary, + color: context.appColors.textTertiary, ), ); }, @@ -189,16 +191,16 @@ class MetricTrendChart extends StatelessWidget { LineChartBarData( spots: spots, isCurved: true, - color: AlarmColors.primary, + color: context.appColors.primary, barWidth: 2, dotData: FlDotData( show: true, getDotPainter: (spot, percent, barData, index) { return FlDotCirclePainter( radius: 3, - color: AlarmColors.danger, + color: context.appColors.danger, strokeWidth: 1, - strokeColor: Colors.white, + strokeColor: context.appColors.cardBackground, ); }, ), @@ -206,8 +208,8 @@ class MetricTrendChart extends StatelessWidget { show: true, gradient: LinearGradient( colors: [ - AlarmColors.primary.withOpacity(0.2), - AlarmColors.primary.withOpacity(0.0), + context.appColors.primary.withOpacity(0.2), + context.appColors.primary.withOpacity(0.0), ], begin: Alignment.topCenter, end: Alignment.bottomCenter, diff --git a/lib/features/v2/workorder/data/datasources/workorder_remote_datasource_impl.dart b/lib/features/v2/workorder/data/datasources/workorder_remote_datasource_impl.dart index 8a3111b5..8c39e914 100644 --- a/lib/features/v2/workorder/data/datasources/workorder_remote_datasource_impl.dart +++ b/lib/features/v2/workorder/data/datasources/workorder_remote_datasource_impl.dart @@ -1,6 +1,5 @@ import 'package:dio/dio.dart'; import '../../../../../core/consts/http_api_consts.dart'; -import '../../../../../core/consts/workorder_consts.dart'; import 'workorder_remote_datasource.dart'; import '../models/workorder_model.dart'; diff --git a/lib/features/v2/workorder/domain/usecases/get_workorder_list_usecase.dart b/lib/features/v2/workorder/domain/usecases/get_workorder_list_usecase.dart index 18d85b92..c2ff6b91 100644 --- a/lib/features/v2/workorder/domain/usecases/get_workorder_list_usecase.dart +++ b/lib/features/v2/workorder/domain/usecases/get_workorder_list_usecase.dart @@ -1,4 +1,3 @@ -import '../../../../../core/consts/workorder_consts.dart'; import '../../../../../core/error/failure.dart'; import '../repositories/workorder_repository.dart'; import '../entities/workorder_entity.dart'; diff --git a/lib/features/v2/workorder/presentation/pages/transfer_dialog.dart b/lib/features/v2/workorder/presentation/pages/transfer_dialog.dart index 1b268b39..0802a544 100644 --- a/lib/features/v2/workorder/presentation/pages/transfer_dialog.dart +++ b/lib/features/v2/workorder/presentation/pages/transfer_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../../../../../core/app/app_user_cubit.dart'; import '../../../../../core/network/dio_client.dart'; import '../../../site/presentation/cubit/site_cubit.dart'; @@ -76,8 +77,8 @@ class _TransferDialogContentState extends State<_TransferDialogContent> { }, builder: (context, state) { return Container( - decoration: const BoxDecoration( - color: Colors.white, + decoration: BoxDecoration( + color: context.appColors.cardBackground, borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), padding: EdgeInsets.only( @@ -107,19 +108,23 @@ class _TransferDialogContentState extends State<_TransferDialogContent> { Widget _buildHeader(BuildContext context) { return Row( children: [ - const Expanded( + Expanded( child: Text( '转派工单', style: TextStyle( fontSize: 17, fontWeight: FontWeight.w600, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), ), GestureDetector( onTap: () => Navigator.of(context).pop(), - child: const Icon(Icons.close, size: 24, color: Color(0xFF86909C)), + child: Icon( + Icons.close, + size: 24, + color: context.appColors.textTertiary, + ), ), ], ); @@ -136,12 +141,15 @@ class _TransferDialogContentState extends State<_TransferDialogContent> { } if (state.users.isEmpty) { - return const Padding( + return Padding( padding: EdgeInsets.all(24), child: Center( child: Text( '暂无可转派人员', - style: TextStyle(color: Color(0xFF86909C), fontSize: 14), + style: TextStyle( + color: context.appColors.textTertiary, + fontSize: 14, + ), ), ), ); @@ -167,12 +175,12 @@ class _TransferDialogContentState extends State<_TransferDialogContent> { margin: const EdgeInsets.only(bottom: 8), decoration: BoxDecoration( color: isSelected - ? const Color(0xFFE8F3FF) - : const Color(0xFFF7F8FA), + ? context.appColors.primary.withOpacity(0.1) + : context.appColors.fillBackground, borderRadius: BorderRadius.circular(8), border: Border.all( color: isSelected - ? const Color(0xFF165DFF) + ? context.appColors.primary : Colors.transparent, ), ), @@ -181,8 +189,8 @@ class _TransferDialogContentState extends State<_TransferDialogContent> { CircleAvatar( radius: 18, backgroundColor: isSelected - ? const Color(0xFF165DFF) - : const Color(0xFFC9CDD4), + ? context.appColors.primary + : context.appColors.textTertiary, child: Text( userName.isNotEmpty ? userName[0] : '?', style: const TextStyle(color: Colors.white, fontSize: 14), @@ -192,16 +200,16 @@ class _TransferDialogContentState extends State<_TransferDialogContent> { Expanded( child: Text( userName.isNotEmpty ? userName : '未知用户', - style: const TextStyle( + style: TextStyle( fontSize: 15, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), ), if (isSelected) - const Icon( + Icon( Icons.check_circle, - color: Color(0xFF165DFF), + color: context.appColors.primary, size: 22, ), ], @@ -217,28 +225,30 @@ class _TransferDialogContentState extends State<_TransferDialogContent> { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '派发备注(选填)', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 8), Container( decoration: BoxDecoration( - color: const Color(0xFFF7F8FA), + color: context.appColors.fillBackground, borderRadius: BorderRadius.circular(8), ), child: TextField( controller: _remarkController, maxLines: 3, maxLength: 200, + style: TextStyle(color: context.appColors.textPrimary), onChanged: (value) => context.read().updateRemark(value), - decoration: const InputDecoration( + decoration: InputDecoration( hintText: '请输入派发备注', + hintStyle: TextStyle(color: context.appColors.textTertiary), border: InputBorder.none, contentPadding: EdgeInsets.all(12), isDense: true, @@ -262,7 +272,7 @@ class _TransferDialogContentState extends State<_TransferDialogContent> { ); }, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF165DFF), + backgroundColor: context.appColors.primary, foregroundColor: Colors.white, elevation: 0, padding: EdgeInsets.zero, @@ -289,24 +299,32 @@ class _TransferDialogContentState extends State<_TransferDialogContent> { showDialog( context: context, builder: (_) => AlertDialog( + backgroundColor: context.appColors.cardBackground, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), content: Column( mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.error_outline, size: 48, color: Color(0xFFF53F3F)), + Icon( + Icons.error_outline, + size: 48, + color: context.appColors.danger, + ), const SizedBox(height: 12), - const Text( + Text( '转派失败', style: TextStyle( fontSize: 17, fontWeight: FontWeight.w600, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 8), Text( message, - style: const TextStyle(fontSize: 14, color: Color(0xFF86909C)), + style: TextStyle( + fontSize: 14, + color: context.appColors.textTertiary, + ), textAlign: TextAlign.center, ), ], @@ -317,7 +335,7 @@ class _TransferDialogContentState extends State<_TransferDialogContent> { child: ElevatedButton( onPressed: () => Navigator.of(context).pop(), style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF165DFF), + backgroundColor: context.appColors.primary, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12), diff --git a/lib/features/v2/workorder/presentation/pages/workorder_detail_page.dart b/lib/features/v2/workorder/presentation/pages/workorder_detail_page.dart index 0cdb7d6b..294a7a1f 100644 --- a/lib/features/v2/workorder/presentation/pages/workorder_detail_page.dart +++ b/lib/features/v2/workorder/presentation/pages/workorder_detail_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:video_player/video_player.dart'; import '../../../../../core/consts/workorder_consts.dart'; import '../cubit/workorder_detail_cubit.dart'; @@ -29,13 +30,18 @@ class _WorkOrderDetailPageState extends State { @override Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; return AnnotatedRegion( - value: SystemUiOverlayStyle.dark.copyWith( - statusBarColor: Colors.transparent, - statusBarIconBrightness: Brightness.dark, - ), + value: isDark + ? SystemUiOverlayStyle.light.copyWith( + statusBarColor: Colors.transparent, + ) + : SystemUiOverlayStyle.dark.copyWith( + statusBarColor: Colors.transparent, + statusBarIconBrightness: Brightness.dark, + ), child: Scaffold( - backgroundColor: WorkOrderColors.pageBackground, + backgroundColor: context.appColors.pageBackground, appBar: _buildAppBar(), body: BlocBuilder( builder: (context, state) { @@ -61,26 +67,26 @@ class _WorkOrderDetailPageState extends State { PreferredSizeWidget _buildAppBar() { return AppBar( - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, elevation: 0, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: WorkOrderColors.primaryText), + icon: Icon(Icons.arrow_back, color: context.appColors.textPrimary), onPressed: () => context.pop(), ), - title: const Text( + title: Text( '工单详情', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: WorkOrderColors.primaryText, + color: context.appColors.textPrimary, ), ), centerTitle: true, actions: [ IconButton( - icon: const Icon( + icon: Icon( Icons.more_horiz, - color: WorkOrderColors.primaryText, + color: context.appColors.textPrimary, ), onPressed: () {}, ), @@ -89,9 +95,9 @@ class _WorkOrderDetailPageState extends State { } Widget _buildLoadingView() { - return const Center( + return Center( child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(WorkOrderColors.primary), + valueColor: AlwaysStoppedAnimation(context.appColors.primary), ), ); } @@ -104,21 +110,21 @@ class _WorkOrderDetailPageState extends State { Icon( Icons.error_outline, size: 48, - color: WorkOrderColors.auxiliaryText, + color: context.appColors.textTertiary, ), const SizedBox(height: 16), Text( message, style: TextStyle( fontSize: 14, - color: WorkOrderColors.auxiliaryText, + color: context.appColors.textTertiary, ), ), const SizedBox(height: 24), ElevatedButton( onPressed: () => _cubit.loadWorkOrderDetail(widget.orderId), style: ElevatedButton.styleFrom( - backgroundColor: WorkOrderColors.primary, + backgroundColor: context.appColors.primary, foregroundColor: Colors.white, ), child: const Text('重试'), @@ -167,18 +173,24 @@ class _WorkOrderDetailPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: WorkOrderColors.cardShadow, + boxShadow: [ + BoxShadow( + color: context.appColors.cardShadow, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '工单编号', style: TextStyle( fontSize: 13, - color: WorkOrderColors.auxiliaryText, + color: context.appColors.textTertiary, ), ), const SizedBox(height: 4), @@ -187,10 +199,10 @@ class _WorkOrderDetailPageState extends State { Expanded( child: Text( workOrder.orderNo, - style: const TextStyle( + style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: WorkOrderColors.primaryText, + color: context.appColors.textPrimary, ), ), ), @@ -201,7 +213,7 @@ class _WorkOrderDetailPageState extends State { vertical: 4, ), decoration: BoxDecoration( - color: const Color(0xFFFF4D4F), + color: context.appColors.danger, borderRadius: BorderRadius.circular(4), ), child: const Text( @@ -218,22 +230,22 @@ class _WorkOrderDetailPageState extends State { const SizedBox(height: 12), Text( workOrder.title, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: WorkOrderColors.primaryText, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 16), _buildInfoRow( '状态', _getStatusLabel(workOrder.status), - valueColor: _getStatusColor(workOrder.status), + valueColor: _getStatusColor(context, workOrder.status), ), _buildInfoRow( '优先级', _getPriorityLabel(workOrder.priority), - valueColor: _getPriorityColor(workOrder.priority), + valueColor: _getPriorityColor(context, workOrder.priority), ), _buildInfoRow('来源', workOrder.source ?? '--'), _buildInfoRow( @@ -250,19 +262,25 @@ class _WorkOrderDetailPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: WorkOrderColors.cardShadow, + boxShadow: [ + BoxShadow( + color: context.appColors.cardShadow, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '基本信息', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: WorkOrderColors.primaryText, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), @@ -294,19 +312,25 @@ class _WorkOrderDetailPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: WorkOrderColors.cardShadow, + boxShadow: [ + BoxShadow( + color: context.appColors.cardShadow, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '设备信息', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: WorkOrderColors.primaryText, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), @@ -321,19 +345,25 @@ class _WorkOrderDetailPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: WorkOrderColors.cardShadow, + boxShadow: [ + BoxShadow( + color: context.appColors.cardShadow, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '时间信息', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: WorkOrderColors.primaryText, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), @@ -359,19 +389,25 @@ class _WorkOrderDetailPageState extends State { width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: WorkOrderColors.cardShadow, + boxShadow: [ + BoxShadow( + color: context.appColors.cardShadow, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '任务描述', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: WorkOrderColors.primaryText, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), @@ -379,7 +415,7 @@ class _WorkOrderDetailPageState extends State { workOrder.taskDescription!, style: TextStyle( fontSize: 14, - color: WorkOrderColors.secondaryText, + color: context.appColors.textSecondary, height: 1.6, ), ), @@ -392,19 +428,25 @@ class _WorkOrderDetailPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: WorkOrderColors.cardShadow, + boxShadow: [ + BoxShadow( + color: context.appColors.cardShadow, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '处理信息', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: WorkOrderColors.primaryText, + color: context.appColors.textPrimary, ), ), const SizedBox(height: 12), @@ -428,21 +470,27 @@ class _WorkOrderDetailPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), - boxShadow: WorkOrderColors.cardShadow, + boxShadow: [ + BoxShadow( + color: context.appColors.cardShadow, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - const Text( + Text( '附件', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: WorkOrderColors.primaryText, + color: context.appColors.textPrimary, ), ), const SizedBox(width: 8), @@ -450,7 +498,7 @@ class _WorkOrderDetailPageState extends State { '(${hasImages ? workOrder.attachments!.length : 0}张图片${hasVideos ? ' ${workOrder.videoUrls!.length}个视频' : ''})', style: TextStyle( fontSize: 14, - color: WorkOrderColors.auxiliaryText, + color: context.appColors.textTertiary, ), ), ], @@ -482,9 +530,9 @@ class _WorkOrderDetailPageState extends State { width: 80, height: 80, decoration: BoxDecoration( - color: const Color(0xFFF7F8FA), + color: context.appColors.fillBackground, borderRadius: BorderRadius.circular(8), - border: Border.all(color: const Color(0xFFE5E6EB)), + border: Border.all(color: context.appColors.divider), ), child: attachment.url.isNotEmpty ? ClipRRect( @@ -496,10 +544,10 @@ class _WorkOrderDetailPageState extends State { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( + Icon( Icons.image_outlined, size: 32, - color: WorkOrderColors.auxiliaryText, + color: context.appColors.textTertiary, ), const SizedBox(height: 4), Text( @@ -508,7 +556,7 @@ class _WorkOrderDetailPageState extends State { : '图片', style: TextStyle( fontSize: 11, - color: WorkOrderColors.auxiliaryText, + color: context.appColors.textTertiary, ), textAlign: TextAlign.center, maxLines: 2, @@ -522,17 +570,17 @@ class _WorkOrderDetailPageState extends State { : Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( + Icon( Icons.image_outlined, size: 32, - color: WorkOrderColors.auxiliaryText, + color: context.appColors.textTertiary, ), const SizedBox(height: 4), Text( attachment.fileName.isNotEmpty ? attachment.fileName : '图片', style: TextStyle( fontSize: 11, - color: WorkOrderColors.auxiliaryText, + color: context.appColors.textTertiary, ), textAlign: TextAlign.center, maxLines: 2, @@ -551,24 +599,24 @@ class _WorkOrderDetailPageState extends State { width: 80, height: 80, decoration: BoxDecoration( - color: const Color(0xFFF7F8FA), + color: context.appColors.fillBackground, borderRadius: BorderRadius.circular(8), - border: Border.all(color: const Color(0xFFE5E6EB)), + border: Border.all(color: context.appColors.divider), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( + Icon( Icons.video_library_outlined, size: 32, - color: WorkOrderColors.primary, + color: context.appColors.primary, ), const SizedBox(height: 4), - const Text( + Text( '视频', style: TextStyle( fontSize: 11, - color: WorkOrderColors.auxiliaryText, + color: context.appColors.textTertiary, ), textAlign: TextAlign.center, maxLines: 2, @@ -644,10 +692,10 @@ class _WorkOrderDetailPageState extends State { width: double.infinity, padding: const EdgeInsets.fromLTRB(16, 10, 16, 16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 8, offset: const Offset(0, -2), ), @@ -658,8 +706,8 @@ class _WorkOrderDetailPageState extends State { child: ElevatedButton( onPressed: () {}, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFF2F3F5), - foregroundColor: const Color(0xFF86909C), + backgroundColor: context.appColors.fillBackground, + foregroundColor: context.appColors.textTertiary, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( @@ -679,10 +727,10 @@ class _WorkOrderDetailPageState extends State { width: double.infinity, padding: const EdgeInsets.fromLTRB(16, 10, 16, 16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 8, offset: const Offset(0, -2), ), @@ -698,10 +746,10 @@ class _WorkOrderDetailPageState extends State { _showTransferDialog(workOrder); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: WorkOrderColors.primary, + backgroundColor: context.appColors.cardBackground, + foregroundColor: context.appColors.primary, elevation: 0, - side: const BorderSide(color: WorkOrderColors.primary), + side: BorderSide(color: context.appColors.primary), padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 12, @@ -728,7 +776,7 @@ class _WorkOrderDetailPageState extends State { ); }, style: ElevatedButton.styleFrom( - backgroundColor: WorkOrderColors.primary, + backgroundColor: context.appColors.primary, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12), @@ -752,10 +800,10 @@ class _WorkOrderDetailPageState extends State { context.push('/workorder/detail/$widget.orderId/process'); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: WorkOrderColors.primary, + backgroundColor: context.appColors.cardBackground, + foregroundColor: context.appColors.primary, elevation: 0, - side: const BorderSide(color: WorkOrderColors.primary), + side: BorderSide(color: context.appColors.primary), padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 12, @@ -806,7 +854,7 @@ class _WorkOrderDetailPageState extends State { label, style: TextStyle( fontSize: 13, - color: WorkOrderColors.auxiliaryText, + color: context.appColors.textTertiary, ), ), const Spacer(), @@ -818,7 +866,7 @@ class _WorkOrderDetailPageState extends State { style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: valueColor ?? WorkOrderColors.primaryText, + color: valueColor ?? context.appColors.textPrimary, ), ), ), @@ -838,14 +886,14 @@ class _WorkOrderDetailPageState extends State { } } - Color _getPriorityColor(WorkOrderPriority priority) { + Color _getPriorityColor(BuildContext context, WorkOrderPriority priority) { switch (priority) { case WorkOrderPriority.high: - return const Color(0xFFFF4D4F); + return context.appColors.danger; case WorkOrderPriority.medium: - return const Color(0xFFFF7D00); + return context.appColors.warning; case WorkOrderPriority.low: - return WorkOrderColors.auxiliaryText; + return context.appColors.textTertiary; } } @@ -862,16 +910,16 @@ class _WorkOrderDetailPageState extends State { } } - Color _getStatusColor(WorkOrderStatus status) { + Color _getStatusColor(BuildContext context, WorkOrderStatus status) { switch (status) { case WorkOrderStatus.pending: - return const Color(0xFFFF7D00); + return context.appColors.warning; case WorkOrderStatus.executing: - return WorkOrderColors.primary; + return context.appColors.primary; case WorkOrderStatus.completed: - return const Color(0xFF00B42A); + return context.appColors.success; case WorkOrderStatus.all: - return WorkOrderColors.auxiliaryText; + return context.appColors.textTertiary; } } diff --git a/lib/features/v2/workorder/presentation/pages/workorder_on_site_page.dart b/lib/features/v2/workorder/presentation/pages/workorder_on_site_page.dart index 2fa78871..678e2b41 100644 --- a/lib/features/v2/workorder/presentation/pages/workorder_on_site_page.dart +++ b/lib/features/v2/workorder/presentation/pages/workorder_on_site_page.dart @@ -4,6 +4,7 @@ import 'package:flutter/services.dart'; import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:video_player/video_player.dart'; import '../../../../../core/consts/workorder_consts.dart'; import '../../domain/entities/workorder_entity.dart'; @@ -101,35 +102,43 @@ class _WorkOrderOnSitePageState extends State { @override Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; return AnnotatedRegion( - value: SystemUiOverlayStyle.dark.copyWith( - statusBarColor: Colors.transparent, - statusBarIconBrightness: Brightness.dark, - ), + value: isDark + ? SystemUiOverlayStyle.light.copyWith( + statusBarColor: Colors.transparent, + ) + : SystemUiOverlayStyle.dark.copyWith( + statusBarColor: Colors.transparent, + statusBarIconBrightness: Brightness.dark, + ), child: Scaffold( - backgroundColor: const Color(0xFFF5F5F5), + backgroundColor: context.appColors.pageBackground, appBar: AppBar( - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, elevation: 0, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Color(0xFF1F2329)), + icon: Icon(Icons.arrow_back, color: context.appColors.textPrimary), onPressed: () => context.pop(_dataChanged), ), - title: const Text( + title: Text( '现场执行', style: TextStyle( fontSize: 17, fontWeight: FontWeight.w600, - color: Color(0xFF1F2329), + color: context.appColors.textPrimary, ), ), centerTitle: true, actions: [ TextButton( onPressed: () {}, - child: const Text( + child: Text( '更多', - style: TextStyle(fontSize: 15, color: Color(0xFF1890FF)), + style: TextStyle( + fontSize: 15, + color: context.appColors.primary, + ), ), ), ], @@ -166,7 +175,7 @@ class _WorkOrderOnSitePageState extends State { width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), ), child: Column( @@ -178,10 +187,10 @@ class _WorkOrderOnSitePageState extends State { Expanded( child: Text( workOrder.orderNo, - style: const TextStyle( + style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF1F2329), + color: context.appColors.textPrimary, ), ), ), @@ -192,7 +201,7 @@ class _WorkOrderOnSitePageState extends State { vertical: 4, ), decoration: BoxDecoration( - color: const Color(0xFFFF4D4F), + color: context.appColors.danger, borderRadius: BorderRadius.circular(4), ), child: const Text( @@ -242,7 +251,10 @@ class _WorkOrderOnSitePageState extends State { children: [ Text( label, - style: const TextStyle(fontSize: 13, color: Color(0xFF86909C)), + style: TextStyle( + fontSize: 13, + color: context.appColors.textTertiary, + ), ), const SizedBox(height: 4), Row( @@ -250,19 +262,19 @@ class _WorkOrderOnSitePageState extends State { Expanded( child: Text( value, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: Color(0xFF1F2329), + color: context.appColors.textPrimary, ), ), ), if (showArrow) - const Padding( + Padding( padding: EdgeInsets.only(left: 8), child: Icon( Icons.chevron_right, size: 18, - color: Color(0xFFBBBBBB), + color: context.appColors.textTertiary, ), ), ], @@ -279,32 +291,38 @@ class _WorkOrderOnSitePageState extends State { children: [ Text( label, - style: const TextStyle(fontSize: 13, color: Color(0xFF86909C)), + style: TextStyle( + fontSize: 13, + color: context.appColors.textTertiary, + ), ), Row( children: [ Text( value, - style: const TextStyle(fontSize: 13, color: Color(0xFF1F2329)), + style: TextStyle( + fontSize: 13, + color: context.appColors.textPrimary, + ), maxLines: 1, overflow: TextOverflow.ellipsis, ), if (showLocation) - const Padding( + Padding( padding: EdgeInsets.only(left: 8), child: Icon( Icons.location_on, size: 16, - color: Color(0xFF1890FF), + color: context.appColors.primary, ), ), if (showArrow) - const Padding( + Padding( padding: EdgeInsets.only(left: 8), child: Icon( Icons.chevron_right, size: 18, - color: Color(0xFFBBBBBB), + color: context.appColors.textTertiary, ), ), ], @@ -327,13 +345,16 @@ class _WorkOrderOnSitePageState extends State { children: [ Text( label, - style: const TextStyle(fontSize: 13, color: Color(0xFF86909C)), + style: TextStyle( + fontSize: 13, + color: context.appColors.textTertiary, + ), ), Text( value, style: TextStyle( fontSize: 13, - color: valueColor ?? const Color(0xFF1F2329), + color: valueColor ?? context.appColors.textPrimary, fontWeight: FontWeight.w500, ), ), @@ -358,13 +379,13 @@ class _WorkOrderOnSitePageState extends State { Color _getStatusColor(WorkOrderStatus status) { switch (status) { case WorkOrderStatus.pending: - return const Color(0xFFFF7D00); + return context.appColors.warning; case WorkOrderStatus.executing: - return const Color(0xFF1890FF); + return context.appColors.primary; case WorkOrderStatus.completed: - return const Color(0xFF00B42A); + return context.appColors.success; case WorkOrderStatus.all: - return const Color(0xFF86909C); + return context.appColors.textTertiary; } } @@ -382,11 +403,11 @@ class _WorkOrderOnSitePageState extends State { Color _getPriorityColor(WorkOrderPriority priority) { switch (priority) { case WorkOrderPriority.high: - return const Color(0xFFFF4D4F); + return context.appColors.danger; case WorkOrderPriority.medium: - return const Color(0xFFFF7D00); + return context.appColors.warning; case WorkOrderPriority.low: - return const Color(0xFF86909C); + return context.appColors.textTertiary; } } @@ -425,18 +446,18 @@ class _WorkOrderOnSitePageState extends State { width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '检查项清单 (5/7)', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, - color: Color(0xFF1F2329), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 16), @@ -457,10 +478,10 @@ class _WorkOrderOnSitePageState extends State { style: TextStyle( fontSize: 14, color: item.checked - ? const Color(0xFF52C41A) + ? context.appColors.success : item.current - ? const Color(0xFF1890FF) - : const Color(0xFF1F2329), + ? context.appColors.primary + : context.appColors.textPrimary, ), ), Container( @@ -470,16 +491,16 @@ class _WorkOrderOnSitePageState extends State { shape: BoxShape.circle, border: Border.all( color: item.checked - ? const Color(0xFF52C41A) + ? context.appColors.success : item.current - ? const Color(0xFF1890FF) - : const Color(0xFFD9D9D9), + ? context.appColors.primary + : context.appColors.divider, width: 2, ), color: item.checked - ? const Color(0xFF52C41A) + ? context.appColors.success : item.current - ? const Color(0xFFE6F7FF) + ? context.appColors.primary.withOpacity(0.1) : Colors.transparent, ), child: item.checked @@ -488,9 +509,9 @@ class _WorkOrderOnSitePageState extends State { ? Container( width: 8, height: 8, - decoration: const BoxDecoration( + decoration: BoxDecoration( shape: BoxShape.circle, - color: Color(0xFF1890FF), + color: context.appColors.primary, ), ) : Container(), @@ -508,18 +529,18 @@ class _WorkOrderOnSitePageState extends State { width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '现场记录', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, - color: Color(0xFF1F2329), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 16), @@ -535,16 +556,23 @@ class _WorkOrderOnSitePageState extends State { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16), decoration: BoxDecoration( - color: const Color(0xFFF5F7FA), + color: context.appColors.fillBackground, borderRadius: BorderRadius.circular(8), ), child: Row( children: [ - const Icon(Icons.mic_none, size: 24, color: Color(0xFFBBBBBB)), + Icon( + Icons.mic_none, + size: 24, + color: context.appColors.textTertiary, + ), const SizedBox(width: 12), - const Text( + Text( '录音备注', - style: TextStyle(fontSize: 13, color: Color(0xFF86909C)), + style: TextStyle( + fontSize: 13, + color: context.appColors.textTertiary, + ), ), ], ), @@ -562,24 +590,30 @@ class _WorkOrderOnSitePageState extends State { children: [ Row( children: [ - const Text( + Text( '现场照片', - style: TextStyle(fontSize: 13, color: Color(0xFF86909C)), + style: TextStyle( + fontSize: 13, + color: context.appColors.textTertiary, + ), ), const SizedBox(width: 8), Text( '(${hasImages ? workOrder.attachments!.length : 0}张图片${hasVideos ? ' ${workOrder.videoUrls!.length}个视频' : ''})', - style: const TextStyle(fontSize: 13, color: Color(0xFF1890FF)), + style: TextStyle(fontSize: 13, color: context.appColors.primary), ), ], ), const SizedBox(height: 12), if (!hasImages && !hasVideos) - const Padding( + Padding( padding: EdgeInsets.symmetric(vertical: 16), child: Text( '暂无照片或视频', - style: TextStyle(fontSize: 13, color: Color(0xFFBBBBBB)), + style: TextStyle( + fontSize: 13, + color: context.appColors.textTertiary, + ), ), ) else @@ -613,8 +647,8 @@ class _WorkOrderOnSitePageState extends State { height: 64, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), - color: const Color(0xFFF7F8FA), - border: Border.all(color: const Color(0xFFE5E6EB)), + color: context.appColors.fillBackground, + border: Border.all(color: context.appColors.divider), ), child: isImage ? ClipRRect( @@ -623,19 +657,19 @@ class _WorkOrderOnSitePageState extends State { url, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { - return const Icon( + return Icon( Icons.image_outlined, size: 24, - color: Color(0xFFBBBBBB), + color: context.appColors.textTertiary, ); }, ), ) - : const Center( + : Center( child: Icon( Icons.video_library_outlined, size: 24, - color: Color(0xFF1890FF), + color: context.appColors.primary, ), ), ), @@ -706,9 +740,13 @@ class _WorkOrderOnSitePageState extends State { height: 64, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), - border: Border.all(color: const Color(0xFFD9D9D9), width: 1), + border: Border.all(color: context.appColors.divider, width: 1), + ), + child: Icon( + Icons.camera_alt, + size: 24, + color: context.appColors.textTertiary, ), - child: const Icon(Icons.camera_alt, size: 24, color: Color(0xFFBBBBBB)), ); } @@ -717,10 +755,10 @@ class _WorkOrderOnSitePageState extends State { width: double.infinity, padding: const EdgeInsets.fromLTRB(16, 10, 16, 16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 8, offset: const Offset(0, -2), ), @@ -734,7 +772,7 @@ class _WorkOrderOnSitePageState extends State { child: ElevatedButton( onPressed: _isOperating ? null : _handleStartWork, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF52C41A), + backgroundColor: context.appColors.success, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12), @@ -770,7 +808,7 @@ class _WorkOrderOnSitePageState extends State { child: ElevatedButton( onPressed: _isOperating ? null : _handleSuspendWork, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFFAAD14), + backgroundColor: context.appColors.warning, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12), @@ -814,7 +852,7 @@ class _WorkOrderOnSitePageState extends State { } }, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF1890FF), + backgroundColor: context.appColors.primary, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12), diff --git a/lib/features/v2/workorder/presentation/pages/workorder_page.dart b/lib/features/v2/workorder/presentation/pages/workorder_page.dart index b954b60e..5e163a96 100644 --- a/lib/features/v2/workorder/presentation/pages/workorder_page.dart +++ b/lib/features/v2/workorder/presentation/pages/workorder_page.dart @@ -7,8 +7,8 @@ import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; import 'package:maibu_satabot_v2/core/network/dio_client.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/features/v2/site/presentation/cubit/site_cubit.dart'; -import '../../../../../core/consts/workorder_consts.dart'; import '../../../site/presentation/widgets/site_selector_widget.dart'; import '../cubit/workorder_cubit.dart'; import '../widgets/workorder_tabbar.dart'; @@ -79,12 +79,13 @@ class _WorkOrderPageState extends State { @override Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; return BlocProvider.value( value: _cubit, child: AnnotatedRegion( - value: SystemUiOverlayStyle.dark, + value: isDark ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark, child: Scaffold( - backgroundColor: const Color(0xFFF7F8FA), + backgroundColor: context.appColors.pageBackground, body: BlocConsumer( listener: (context, state) { if (state is WorkOrderLoaded && state.errorMessage != null) { @@ -115,7 +116,7 @@ class _WorkOrderPageState extends State { Widget _buildLoadingView() { return Center( child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(AppConstants.primaryColor), + valueColor: AlwaysStoppedAnimation(context.appColors.primary), ), ); } @@ -128,14 +129,14 @@ class _WorkOrderPageState extends State { Icon( Icons.inbox_outlined, size: 48, - color: AppConstants.auxiliaryTextColor, + color: context.appColors.textTertiary, ), const SizedBox(height: 16), Text( '无数据', style: TextStyle( fontSize: 14, - color: AppConstants.auxiliaryTextColor, + color: context.appColors.textTertiary, ), ), ], @@ -195,14 +196,18 @@ class _WorkOrderPageState extends State { children: [ const Flexible(child: SiteSelectorWidget(compact: true)), const SizedBox(width: 8), - Container(width: 1, height: 20, color: const Color(0xFFE5E6EB)), + Container( + width: 1, + height: 20, + color: context.appColors.divider, + ), const SizedBox(width: 8), Text( AppLocalizations.of(context).translate('work_order_v2.title'), - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: Color(0xFF86909C), + color: context.appColors.textTertiary, ), ), ], @@ -210,10 +215,10 @@ class _WorkOrderPageState extends State { ), GestureDetector( onTap: () => context.push('/workorder/report'), - child: const Icon( + child: Icon( Icons.bar_chart, size: 24, - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, ), ), ], @@ -237,17 +242,23 @@ class _WorkOrderPageState extends State { child: Center(child: CircularProgressIndicator()), ); } else if (state.hasMore) { - return const Padding( + return Padding( padding: EdgeInsets.symmetric(vertical: 16), child: Center( - child: Text('上滑加载更多', style: TextStyle(color: Color(0xFF86909C))), + child: Text( + '上滑加载更多', + style: TextStyle(color: context.appColors.textTertiary), + ), ), ); } else { - return const Padding( + return Padding( padding: EdgeInsets.symmetric(vertical: 16), child: Center( - child: Text('已加载全部数据', style: TextStyle(color: Color(0xFF86909C))), + child: Text( + '已加载全部数据', + style: TextStyle(color: context.appColors.textTertiary), + ), ), ); } diff --git a/lib/features/v2/workorder/presentation/pages/workorder_process_page.dart b/lib/features/v2/workorder/presentation/pages/workorder_process_page.dart index 536ab491..5f2ffd3a 100644 --- a/lib/features/v2/workorder/presentation/pages/workorder_process_page.dart +++ b/lib/features/v2/workorder/presentation/pages/workorder_process_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; class WorkOrderProcessPage extends StatelessWidget { final String orderId; @@ -9,26 +10,26 @@ class WorkOrderProcessPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: const Color(0xFFF5F5F5), + backgroundColor: context.appColors.pageBackground, appBar: AppBar( - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, elevation: 0, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Color(0xFF1F2329)), + icon: Icon(Icons.arrow_back, color: context.appColors.textPrimary), onPressed: () => context.pop(), ), - title: const Text( + title: Text( '工单流程', style: TextStyle( fontSize: 17, fontWeight: FontWeight.w600, - color: Color(0xFF1F2329), + color: context.appColors.textPrimary, ), ), centerTitle: true, actions: [ IconButton( - icon: const Icon(Icons.share, color: Color(0xFF86909C)), + icon: Icon(Icons.share, color: context.appColors.textTertiary), onPressed: () {}, ), ], @@ -37,18 +38,18 @@ class WorkOrderProcessPage extends StatelessWidget { padding: const EdgeInsets.all(16), child: Column( children: [ - _buildProcessTimeline(), + _buildProcessTimeline(context), const SizedBox(height: 24), - _buildExecutionRecords(), + _buildExecutionRecords(context), const SizedBox(height: 32), ], ), ), - bottomNavigationBar: _buildBottomIndicator(), + bottomNavigationBar: _buildBottomIndicator(context), ); } - Widget _buildProcessTimeline() { + Widget _buildProcessTimeline(BuildContext context) { final processes = [ _ProcessStep( status: ProcessStatus.completed, @@ -92,7 +93,7 @@ class WorkOrderProcessPage extends StatelessWidget { width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), ), child: Column( @@ -102,13 +103,14 @@ class WorkOrderProcessPage extends StatelessWidget { final isLast = index == processes.length - 1; final nextStatus = isLast ? null : processes[index + 1].status; - return _buildProcessItem(step, nextStatus, isLast); + return _buildProcessItem(context, step, nextStatus, isLast); }).toList(), ), ); } Widget _buildProcessItem( + BuildContext context, _ProcessStep step, ProcessStatus? nextStatus, bool isLast, @@ -120,8 +122,8 @@ class WorkOrderProcessPage extends StatelessWidget { children: [ Column( children: [ - _buildStatusIcon(step.status, 0), - if (showLine) _buildProcessLine(step.status, nextStatus!), + _buildStatusIcon(context, step.status, 0), + if (showLine) _buildProcessLine(context, step.status, nextStatus!), ], ), const SizedBox(width: 12), @@ -137,8 +139,8 @@ class WorkOrderProcessPage extends StatelessWidget { fontSize: 14, fontWeight: FontWeight.w600, color: step.status == ProcessStatus.completed - ? const Color(0xFF1F2329) - : const Color(0xFFBBBBBB), + ? context.appColors.textPrimary + : context.appColors.textTertiary, ), ), if (step.time.isNotEmpty) ...[ @@ -148,8 +150,8 @@ class WorkOrderProcessPage extends StatelessWidget { style: TextStyle( fontSize: 12, color: step.status == ProcessStatus.completed - ? const Color(0xFF86909C) - : const Color(0xFFBBBBBB), + ? context.appColors.textTertiary + : context.appColors.textTertiary, ), ), ], @@ -162,8 +164,8 @@ class WorkOrderProcessPage extends StatelessWidget { style: TextStyle( fontSize: 12, color: step.status == ProcessStatus.completed - ? const Color(0xFF86909C) - : const Color(0xFFBBBBBB), + ? context.appColors.textTertiary + : context.appColors.textTertiary, ), ), ), @@ -175,7 +177,7 @@ class WorkOrderProcessPage extends StatelessWidget { ); } - Widget _buildStatusIcon(ProcessStatus status, int index) { + Widget _buildStatusIcon(BuildContext context, ProcessStatus status, int index) { Widget icon; Color iconColor; Color backgroundColor; @@ -183,18 +185,18 @@ class WorkOrderProcessPage extends StatelessWidget { switch (status) { case ProcessStatus.completed: icon = const Icon(Icons.check, size: 16); - iconColor = const Color(0xFF52C41A); - backgroundColor = const Color(0xFFF6FFED); + iconColor = context.appColors.success; + backgroundColor = context.appColors.success.withOpacity(0.1); break; case ProcessStatus.current: icon = const Icon(Icons.check, size: 16); - iconColor = const Color(0xFF1890FF); - backgroundColor = const Color(0xFFE6F7FF); + iconColor = context.appColors.primary; + backgroundColor = context.appColors.primary.withOpacity(0.1); break; case ProcessStatus.pending: icon = Container(); - iconColor = const Color(0xFFBBBBBB); - backgroundColor = const Color(0xFFFAFAFA); + iconColor = context.appColors.textTertiary; + backgroundColor = context.appColors.fillBackground; break; } @@ -211,18 +213,19 @@ class WorkOrderProcessPage extends StatelessWidget { } Widget _buildProcessLine( + BuildContext context, ProcessStatus currentStatus, ProcessStatus nextStatus, ) { Color lineColor; if (currentStatus == ProcessStatus.completed && nextStatus == ProcessStatus.completed) { - lineColor = const Color(0xFF52C41A); + lineColor = context.appColors.success; } else if (currentStatus == ProcessStatus.completed && nextStatus == ProcessStatus.current) { - lineColor = const Color(0xFF1890FF); + lineColor = context.appColors.primary; } else { - lineColor = const Color(0xFFE8E8E8); + lineColor = context.appColors.divider; } return Container( @@ -233,7 +236,7 @@ class WorkOrderProcessPage extends StatelessWidget { ); } - Widget _buildExecutionRecords() { + Widget _buildExecutionRecords(BuildContext context) { final records = [ _ExecutionRecord(time: '2025-05-21 09:20', action: '到达现场'), _ExecutionRecord(time: '2025-05-21 09:35', action: '现场定位打卡'), @@ -249,41 +252,47 @@ class WorkOrderProcessPage extends StatelessWidget { width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '执行记录', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, - color: Color(0xFF1F2329), + color: context.appColors.textPrimary, ), ), const SizedBox(height: 16), - ...records.map((record) => _buildRecordItem(record)), + ...records.map((record) => _buildRecordItem(context, record)), ], ), ); } - Widget _buildRecordItem(_ExecutionRecord record) { + Widget _buildRecordItem(BuildContext context, _ExecutionRecord record) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ Text( record.time, - style: const TextStyle(fontSize: 12, color: Color(0xFF86909C)), + style: TextStyle( + fontSize: 12, + color: context.appColors.textTertiary, + ), ), const SizedBox(width: 32), Expanded( child: Text( record.action, - style: const TextStyle(fontSize: 13, color: Color(0xFF1F2329)), + style: TextStyle( + fontSize: 13, + color: context.appColors.textPrimary, + ), ), ), ], @@ -291,15 +300,15 @@ class WorkOrderProcessPage extends StatelessWidget { ); } - Widget _buildBottomIndicator() { + Widget _buildBottomIndicator(BuildContext context) { return Container( width: double.infinity, padding: const EdgeInsets.fromLTRB(16, 10, 16, 16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: context.appColors.cardShadow, blurRadius: 8, offset: const Offset(0, -2), ), @@ -309,8 +318,8 @@ class WorkOrderProcessPage extends StatelessWidget { height: 48, child: Container( width: double.infinity, - decoration: const BoxDecoration( - color: Color(0xFF1890FF), + decoration: BoxDecoration( + color: context.appColors.primary, borderRadius: BorderRadius.all(Radius.circular(8)), ), child: const Center( diff --git a/lib/features/v2/workorder/presentation/pages/workorder_receipt_page.dart b/lib/features/v2/workorder/presentation/pages/workorder_receipt_page.dart index 4109b981..1d604c06 100644 --- a/lib/features/v2/workorder/presentation/pages/workorder_receipt_page.dart +++ b/lib/features/v2/workorder/presentation/pages/workorder_receipt_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../../domain/entities/workorder_entity.dart'; import '../../../work_order/domain/usecases/work_order_usecases.dart'; @@ -45,18 +46,18 @@ class _WorkOrderReceiptPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: const Color(0xFFF7F8FA), + backgroundColor: context.appColors.pageBackground, appBar: AppBar( - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, elevation: 0.5, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Color(0xFF1D2129)), + icon: Icon(Icons.arrow_back, color: context.appColors.textPrimary), onPressed: () => context.pop(), ), - title: const Text( + title: Text( '工单回执', style: TextStyle( - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, fontSize: 17, fontWeight: FontWeight.w600, ), @@ -65,10 +66,10 @@ class _WorkOrderReceiptPageState extends State { actions: [ TextButton( onPressed: _isSubmitting ? null : _handleSubmit, - child: const Text( + child: Text( '提交', style: TextStyle( - color: Color(0xFF165DFF), + color: context.appColors.primary, fontSize: 15, fontWeight: FontWeight.w500, ), @@ -183,19 +184,22 @@ class _WorkOrderReceiptPageState extends State { Widget _buildOrderNumberCard() { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), - color: Colors.white, + color: context.appColors.cardBackground, child: Row( children: [ - const Text( + Text( '工单编号', - style: TextStyle(color: Color(0xFF4E5969), fontSize: 14), + style: TextStyle( + color: context.appColors.textSecondary, + fontSize: 14, + ), ), const SizedBox(width: 12), Expanded( child: Text( widget.workOrder.orderNo, - style: const TextStyle( - color: Color(0xFF1D2129), + style: TextStyle( + color: context.appColors.textPrimary, fontSize: 14, fontWeight: FontWeight.w500, ), @@ -211,7 +215,7 @@ class _WorkOrderReceiptPageState extends State { const results = ['已解决', '部分解决', '未解决']; return Container( padding: const EdgeInsets.all(16), - color: Colors.white, + color: context.appColors.cardBackground, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -228,16 +232,22 @@ class _WorkOrderReceiptPageState extends State { padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), - color: selected ? const Color(0xFF165DFF) : Colors.white, + color: selected + ? context.appColors.primary + : context.appColors.cardBackground, border: Border.all( - color: selected ? const Color(0xFF165DFF) : const Color(0xFFE5E6EB), + color: selected + ? context.appColors.primary + : context.appColors.divider, ), ), child: Text( results[index], style: TextStyle( fontSize: 13, - color: selected ? Colors.white : const Color(0xFF4E5969), + color: selected + ? Colors.white + : context.appColors.textSecondary, fontWeight: selected ? FontWeight.w500 : FontWeight.normal, ), ), @@ -260,7 +270,7 @@ class _WorkOrderReceiptPageState extends State { }) { return Container( padding: const EdgeInsets.all(16), - color: Colors.white, + color: context.appColors.cardBackground, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -268,27 +278,27 @@ class _WorkOrderReceiptPageState extends State { const SizedBox(height: 10), TextField( controller: controller, - style: const TextStyle(fontSize: 14, color: Color(0xFF1D2129)), + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary), decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), filled: true, - fillColor: const Color(0xFFF7F8FA), + fillColor: context.appColors.fillBackground, border: OutlineInputBorder( borderRadius: BorderRadius.circular(6), - borderSide: const BorderSide(color: Color(0xFFE5E6EB)), + borderSide: BorderSide(color: context.appColors.divider), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), - borderSide: const BorderSide(color: Color(0xFFE5E6EB)), + borderSide: BorderSide(color: context.appColors.divider), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), - borderSide: const BorderSide(color: Color(0xFF165DFF)), + borderSide: BorderSide(color: context.appColors.primary), ), isDense: true, suffixIcon: PopupMenuButton( padding: EdgeInsets.zero, - icon: const Icon(Icons.keyboard_arrow_down, color: Color(0xFF86909C), size: 20), + icon: Icon(Icons.keyboard_arrow_down, color: context.appColors.textTertiary, size: 20), onSelected: (value) { controller.text = value; }, @@ -310,7 +320,7 @@ class _WorkOrderReceiptPageState extends State { }) { return Container( padding: const EdgeInsets.all(16), - color: Colors.white, + color: context.appColors.cardBackground, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -319,24 +329,24 @@ class _WorkOrderReceiptPageState extends State { TextField( controller: controller, maxLines: maxLines, - style: const TextStyle(fontSize: 14, color: Color(0xFF1D2129), height: 1.5), + style: TextStyle(fontSize: 14, color: context.appColors.textPrimary, height: 1.5), decoration: InputDecoration( hintText: hintText, - hintStyle: const TextStyle(color: Color(0xFFC9CDD4), fontSize: 14), + hintStyle: TextStyle(color: context.appColors.textTertiary, fontSize: 14), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), filled: true, - fillColor: const Color(0xFFF7F8FA), + fillColor: context.appColors.fillBackground, border: OutlineInputBorder( borderRadius: BorderRadius.circular(6), - borderSide: const BorderSide(color: Color(0xFFE5E6EB)), + borderSide: BorderSide(color: context.appColors.divider), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), - borderSide: const BorderSide(color: Color(0xFFE5E6EB)), + borderSide: BorderSide(color: context.appColors.divider), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), - borderSide: const BorderSide(color: Color(0xFF165DFF)), + borderSide: BorderSide(color: context.appColors.primary), ), isDense: true, ), @@ -355,16 +365,19 @@ class _WorkOrderReceiptPageState extends State { return Container( padding: const EdgeInsets.all(16), - color: Colors.white, + color: context.appColors.cardBackground, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLabel('现场照片', required: true), const SizedBox(height: 12), if (!hasImages && !hasVideos) - const Text( + Text( '暂无照片或视频', - style: TextStyle(fontSize: 13, color: Color(0xFFBBBBBB)), + style: TextStyle( + fontSize: 13, + color: context.appColors.textTertiary, + ), ) else Wrap( @@ -394,8 +407,11 @@ class _WorkOrderReceiptPageState extends State { return Container( width: 72, height: 72, - color: const Color(0xFFF7F8FA), - child: const Icon(Icons.broken_image, color: Color(0xFFBBBBBB)), + color: this.context.appColors.fillBackground, + child: Icon( + Icons.broken_image, + color: this.context.appColors.textTertiary, + ), ); }, ), @@ -407,11 +423,15 @@ class _WorkOrderReceiptPageState extends State { width: 72, height: 72, decoration: BoxDecoration( - color: const Color(0xFFF7F8FA), + color: context.appColors.fillBackground, borderRadius: BorderRadius.circular(6), - border: Border.all(color: const Color(0xFFE5E6EB)), + border: Border.all(color: context.appColors.divider), + ), + child: Icon( + Icons.video_library_outlined, + size: 24, + color: context.appColors.primary, ), - child: const Icon(Icons.video_library_outlined, size: 24, color: Color(0xFF1890FF)), ); } @@ -424,11 +444,15 @@ class _WorkOrderReceiptPageState extends State { width: 72, height: 72, decoration: BoxDecoration( - color: const Color(0xFFF7F8FA), + color: context.appColors.fillBackground, borderRadius: BorderRadius.circular(6), - border: Border.all(color: const Color(0xFFE5E6EB)), + border: Border.all(color: context.appColors.divider), + ), + child: Icon( + Icons.add, + color: context.appColors.textTertiary, + size: 28, ), - child: const Icon(Icons.add, color: Color(0xFFC9CDD4), size: 28), ), ); } @@ -437,7 +461,7 @@ class _WorkOrderReceiptPageState extends State { Widget _buildSignatureCard() { return Container( padding: const EdgeInsets.all(16), - color: Colors.white, + color: context.appColors.cardBackground, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -449,9 +473,12 @@ class _WorkOrderReceiptPageState extends State { onTap: () { // TODO: 清除签名 }, - child: const Text( + child: Text( '清空', - style: TextStyle(color: Color(0xFF165DFF), fontSize: 13), + style: TextStyle( + color: context.appColors.primary, + fontSize: 13, + ), ), ), ], @@ -461,14 +488,18 @@ class _WorkOrderReceiptPageState extends State { height: 100, width: double.infinity, decoration: BoxDecoration( - color: const Color(0xFFF7F8FA), + color: context.appColors.fillBackground, borderRadius: BorderRadius.circular(6), - border: Border.all(color: const Color(0xFFE5E6EB)), + border: Border.all(color: context.appColors.divider), ), - child: const Center( + child: Center( child: Text( '李建国', - style: TextStyle(fontSize: 24, color: Color(0xFF1D2129), fontFamily: 'KaiTi'), + style: TextStyle( + fontSize: 24, + color: context.appColors.textPrimary, + fontFamily: 'KaiTi', + ), ), ), ), @@ -482,14 +513,14 @@ class _WorkOrderReceiptPageState extends State { return Row( children: [ if (required) - const Text( + Text( '* ', - style: TextStyle(color: Color(0xFFF53F3F), fontSize: 14), + style: TextStyle(color: context.appColors.danger, fontSize: 14), ), Text( text, - style: const TextStyle( - color: Color(0xFF4E5969), + style: TextStyle( + color: context.appColors.textSecondary, fontSize: 14, fontWeight: FontWeight.w500, ), @@ -507,16 +538,18 @@ class _WorkOrderReceiptPageState extends State { top: 12, bottom: 12 + MediaQuery.of(context).padding.bottom, ), - decoration: const BoxDecoration( - color: Colors.white, - border: Border(top: BorderSide(color: Color(0xFFE5E6EB), width: 0.5)), + decoration: BoxDecoration( + color: context.appColors.cardBackground, + border: Border( + top: BorderSide(color: context.appColors.divider, width: 0.5), + ), ), child: SizedBox( height: 48, child: ElevatedButton( onPressed: _isSubmitting ? null : _handleSubmit, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF165DFF), + backgroundColor: context.appColors.primary, foregroundColor: Colors.white, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), diff --git a/lib/features/v2/workorder/presentation/pages/workorder_report_page.dart b/lib/features/v2/workorder/presentation/pages/workorder_report_page.dart index 32d7e9e6..624d40aa 100644 --- a/lib/features/v2/workorder/presentation/pages/workorder_report_page.dart +++ b/lib/features/v2/workorder/presentation/pages/workorder_report_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:go_router/go_router.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; class WorkOrderReportPage extends StatefulWidget { const WorkOrderReportPage({super.key}); @@ -37,18 +38,18 @@ class _WorkOrderReportPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: const Color(0xFFF7F8FA), + backgroundColor: context.appColors.pageBackground, appBar: AppBar( - backgroundColor: Colors.white, + backgroundColor: context.appColors.cardBackground, elevation: 0.5, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Color(0xFF1D2129)), + icon: Icon(Icons.arrow_back, color: context.appColors.textPrimary), onPressed: () => context.pop(), ), - title: const Text( + title: Text( '移动报表概览', style: TextStyle( - color: Color(0xFF1D2129), + color: context.appColors.textPrimary, fontSize: 17, fontWeight: FontWeight.w600, ), @@ -57,10 +58,10 @@ class _WorkOrderReportPageState extends State { actions: [ TextButton( onPressed: () {}, - child: const Text( + child: Text( '更多', style: TextStyle( - color: Color(0xFF165DFF), + color: context.appColors.primary, fontSize: 14, ), ), @@ -93,16 +94,27 @@ class _WorkOrderReportPageState extends State { Widget _buildTimeRange() { return Row( children: [ - const Icon(Icons.calendar_today, size: 16, color: Color(0xFF4E5969)), + Icon( + Icons.calendar_today, + size: 16, + color: context.appColors.textSecondary, + ), const SizedBox(width: 6), - const Text( + Text( '本周 05-19 ~ 05-25', - style: TextStyle(fontSize: 14, color: Color(0xFF4E5969)), + style: TextStyle( + fontSize: 14, + color: context.appColors.textSecondary, + ), ), const Spacer(), GestureDetector( onTap: () {}, - child: const Icon(Icons.keyboard_arrow_down, size: 20, color: Color(0xFF86909C)), + child: Icon( + Icons.keyboard_arrow_down, + size: 20, + color: context.appColors.textTertiary, + ), ), ], ); @@ -112,11 +124,11 @@ class _WorkOrderReportPageState extends State { Widget _buildMetricCards() { return Row( children: [ - Expanded(child: _metricCard('个人工单总数', '$_totalOrders', '单', const Color(0xFFE8F3FF), const Color(0xFF165DFF))), + Expanded(child: _metricCard('个人工单总数', '$_totalOrders', '单', context.appColors.primary.withOpacity(0.1), context.appColors.primary)), const SizedBox(width: 10), - Expanded(child: _metricCard('已完成', '$_completedOrders', '单', const Color(0xFFE8FFEA), const Color(0xFF00B42A))), + Expanded(child: _metricCard('已完成', '$_completedOrders', '单', context.appColors.success.withOpacity(0.1), context.appColors.success)), const SizedBox(width: 10), - Expanded(child: _metricCard('完成率', '$_completionRate', '%', const Color(0xFFFFF3E0), const Color(0xFFFF7D00))), + Expanded(child: _metricCard('完成率', '$_completionRate', '%', context.appColors.warning.withOpacity(0.1), context.appColors.warning)), ], ); } @@ -131,7 +143,7 @@ class _WorkOrderReportPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(title, style: const TextStyle(fontSize: 12, color: Color(0xFF86909C))), + Text(title, style: TextStyle(fontSize: 12, color: context.appColors.textTertiary)), const SizedBox(height: 8), RichText( text: TextSpan( @@ -157,13 +169,13 @@ class _WorkOrderReportPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('工单类型分布', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF1D2129))), + Text('工单类型分布', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: context.appColors.textPrimary)), const SizedBox(height: 16), Row( children: [ @@ -204,10 +216,10 @@ class _WorkOrderReportPageState extends State { ), ), const SizedBox(width: 8), - Expanded(child: Text(d.label, style: const TextStyle(fontSize: 13, color: Color(0xFF4E5969)))), + Expanded(child: Text(d.label, style: TextStyle(fontSize: 13, color: context.appColors.textSecondary))), Text( '${d.value.toStringAsFixed(0)}%', - style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF1D2129)), + style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: context.appColors.textPrimary), ), ], ), @@ -227,7 +239,7 @@ class _WorkOrderReportPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), ), child: Column( @@ -235,11 +247,11 @@ class _WorkOrderReportPageState extends State { children: [ Row( children: [ - const Text('本周趋势', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF1D2129))), + Text('本周趋势', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: context.appColors.textPrimary)), const Spacer(), - _legendDot(const Color(0xFF165DFF), '创建数'), + _legendDot(context.appColors.primary, '创建数'), const SizedBox(width: 12), - _legendDot(const Color(0xFF00B42A), '完成数'), + _legendDot(context.appColors.success, '完成数'), ], ), const SizedBox(height: 16), @@ -255,13 +267,13 @@ class _WorkOrderReportPageState extends State { barRods: [ BarChartRodData( toY: _createdData[i].toDouble(), - color: const Color(0xFF165DFF), + color: context.appColors.primary, width: 10, borderRadius: const BorderRadius.vertical(top: Radius.circular(3)), ), BarChartRodData( toY: _completedData[i].toDouble(), - color: const Color(0xFF00B42A), + color: context.appColors.success, width: 10, borderRadius: const BorderRadius.vertical(top: Radius.circular(3)), ), @@ -275,7 +287,7 @@ class _WorkOrderReportPageState extends State { reservedSize: 30, getTitlesWidget: (value, meta) { if (value % 6 == 0) { - return Text('${value.toInt()}', style: const TextStyle(fontSize: 11, color: Color(0xFF86909C))); + return Text('${value.toInt()}', style: TextStyle(fontSize: 11, color: context.appColors.textTertiary)); } return const SizedBox.shrink(); }, @@ -289,7 +301,7 @@ class _WorkOrderReportPageState extends State { if (i >= 0 && i < _trendDays.length) { return Padding( padding: const EdgeInsets.only(top: 6), - child: Text(_trendDays[i], style: const TextStyle(fontSize: 10, color: Color(0xFF86909C))), + child: Text(_trendDays[i], style: TextStyle(fontSize: 10, color: context.appColors.textTertiary)), ); } return const SizedBox.shrink(); @@ -304,7 +316,7 @@ class _WorkOrderReportPageState extends State { drawVerticalLine: false, horizontalInterval: 6, getDrawingHorizontalLine: (value) => FlLine( - color: const Color(0xFFF0F0F0), + color: context.appColors.divider, strokeWidth: 1, ), ), @@ -323,7 +335,7 @@ class _WorkOrderReportPageState extends State { children: [ Container(width: 8, height: 8, decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(2))), const SizedBox(width: 4), - Text(label, style: const TextStyle(fontSize: 12, color: Color(0xFF86909C))), + Text(label, style: TextStyle(fontSize: 12, color: context.appColors.textTertiary)), ], ); } @@ -333,13 +345,13 @@ class _WorkOrderReportPageState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('待处理工单 TOP5', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF1D2129))), + Text('待处理工单 TOP5', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: context.appColors.textPrimary)), const SizedBox(height: 12), ...List.generate(_topPendingList.length, (i) { final item = _topPendingList[i]; @@ -352,7 +364,9 @@ class _WorkOrderReportPageState extends State { width: 22, height: 22, decoration: BoxDecoration( - color: i < 3 ? const Color(0xFF165DFF) : const Color(0xFFC9CDD4), + color: i < 3 + ? context.appColors.primary + : context.appColors.fillBackground, borderRadius: BorderRadius.circular(4), ), child: Center( @@ -361,7 +375,9 @@ class _WorkOrderReportPageState extends State { style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, - color: i < 3 ? Colors.white : const Color(0xFF86909C), + color: i < 3 + ? Colors.white + : context.appColors.textTertiary, ), ), ), @@ -371,23 +387,27 @@ class _WorkOrderReportPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(item.title, style: const TextStyle(fontSize: 14, color: Color(0xFF1D2129))), + Text(item.title, style: TextStyle(fontSize: 14, color: context.appColors.textPrimary)), const SizedBox(height: 2), - Text(item.area, style: const TextStyle(fontSize: 12, color: Color(0xFF86909C))), + Text(item.area, style: TextStyle(fontSize: 12, color: context.appColors.textTertiary)), ], ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( - color: isUrgent ? const Color(0xFFFFECE8) : const Color(0xFFF7F8FA), + color: isUrgent + ? context.appColors.danger.withOpacity(0.1) + : context.appColors.fillBackground, borderRadius: BorderRadius.circular(4), ), child: Text( '剩余${item.remainHours}h', style: TextStyle( fontSize: 12, - color: isUrgent ? const Color(0xFFF53F3F) : const Color(0xFF86909C), + color: isUrgent + ? context.appColors.danger + : context.appColors.textTertiary, fontWeight: FontWeight.w500, ), ), @@ -411,8 +431,8 @@ class _WorkOrderReportPageState extends State { icon: const Icon(Icons.download, size: 18), label: const Text('导出报表', style: TextStyle(fontSize: 14)), style: OutlinedButton.styleFrom( - foregroundColor: const Color(0xFF165DFF), - side: const BorderSide(color: Color(0xFF165DFF)), + foregroundColor: context.appColors.primary, + side: BorderSide(color: context.appColors.primary), padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), @@ -425,7 +445,7 @@ class _WorkOrderReportPageState extends State { icon: const Icon(Icons.share, size: 18), label: const Text('分享', style: TextStyle(fontSize: 14)), style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF165DFF), + backgroundColor: context.appColors.primary, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12), diff --git a/lib/features/v2/workorder/presentation/widgets/workorder_count_card.dart b/lib/features/v2/workorder/presentation/widgets/workorder_count_card.dart index eaba2f72..75884219 100644 --- a/lib/features/v2/workorder/presentation/widgets/workorder_count_card.dart +++ b/lib/features/v2/workorder/presentation/widgets/workorder_count_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../../../../../core/consts/workorder_consts.dart'; import '../../domain/entities/workorder_entity.dart'; @@ -18,41 +19,51 @@ class WorkOrderCountCard extends StatelessWidget { ), padding: const EdgeInsets.all(20), decoration: BoxDecoration( - color: AppConstants.backgroundColor, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(AppConstants.borderRadius), - boxShadow: AppConstants.cardShadow, + boxShadow: [ + BoxShadow( + color: context.appColors.cardShadow, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), child: Row( children: [ _buildCountItem( + context, label: AppLocalizations.of( context, ).translate('work_order_v2.pending'), value: count.pendingCount.toString(), - valueColor: WorkOrderColors.primaryText, + valueColor: context.appColors.textPrimary, ), - _buildDivider(), + _buildDivider(context), _buildCountItem( + context, label: AppLocalizations.of( context, ).translate('work_order_v2.executing'), value: count.executingCount.toString(), - valueColor: WorkOrderColors.primaryText, + valueColor: context.appColors.textPrimary, ), - _buildDivider(), + _buildDivider(context), _buildCountItem( + context, label: AppLocalizations.of( context, ).translate('work_order_v2.today_completed'), value: count.todayCompletedCount.toString(), - valueColor: WorkOrderColors.primary, + valueColor: context.appColors.primary, ), ], ), ); } - Widget _buildCountItem({ + Widget _buildCountItem( + BuildContext context, { required String label, required String value, required Color valueColor, @@ -64,7 +75,7 @@ class WorkOrderCountCard extends StatelessWidget { label, style: TextStyle( fontSize: 13, - color: AppConstants.auxiliaryTextColor, + color: context.appColors.textTertiary, ), ), const SizedBox(height: 8), @@ -81,7 +92,11 @@ class WorkOrderCountCard extends StatelessWidget { ); } - Widget _buildDivider() { - return Container(width: 1, height: 40, color: const Color(0xFFF0F0F0)); + Widget _buildDivider(BuildContext context) { + return Container( + width: 1, + height: 40, + color: context.appColors.divider, + ); } } diff --git a/lib/features/v2/workorder/presentation/widgets/workorder_item_card.dart b/lib/features/v2/workorder/presentation/widgets/workorder_item_card.dart index c679f0e2..97319d65 100644 --- a/lib/features/v2/workorder/presentation/widgets/workorder_item_card.dart +++ b/lib/features/v2/workorder/presentation/widgets/workorder_item_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../../../../../core/consts/workorder_consts.dart'; import '../../domain/entities/workorder_entity.dart'; @@ -22,9 +23,15 @@ class WorkOrderItemCard extends StatelessWidget { ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppConstants.backgroundColor, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(AppConstants.borderRadius), - boxShadow: AppConstants.cardShadow, + boxShadow: [ + BoxShadow( + color: context.appColors.cardShadow, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), child: Stack( children: [ @@ -41,7 +48,7 @@ class WorkOrderItemCard extends StatelessWidget { style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: AppConstants.primaryTextColor, + color: context.appColors.textPrimary, ), ), ), @@ -52,6 +59,7 @@ class WorkOrderItemCard extends StatelessWidget { // 详情信息 _buildDetailRow( + context, AppLocalizations.of( context, ).translate('work_order_v2.order_no'), @@ -61,17 +69,19 @@ class WorkOrderItemCard extends StatelessWidget { // 优先级 _buildDetailRow( + context, AppLocalizations.of( context, ).translate('work_order_v2.priority'), _getPriorityText(context, workOrder.priority), - valueColor: _getPriorityColor(workOrder.priority), + valueColor: _getPriorityColor(context, workOrder.priority), ), const SizedBox(height: 6), // 根据状态显示不同信息 if (workOrder.status == WorkOrderStatus.pending) ...[ _buildDetailRow( + context, AppLocalizations.of( context, ).translate('work_order_v2.create_time'), @@ -79,6 +89,7 @@ class WorkOrderItemCard extends StatelessWidget { ), const SizedBox(height: 6), _buildDetailRow( + context, AppLocalizations.of( context, ).translate('work_order_v2.location'), @@ -86,6 +97,7 @@ class WorkOrderItemCard extends StatelessWidget { ), ] else if (workOrder.status == WorkOrderStatus.executing) ...[ _buildDetailRow( + context, AppLocalizations.of( context, ).translate('work_order_v2.executor'), @@ -93,6 +105,7 @@ class WorkOrderItemCard extends StatelessWidget { ), const SizedBox(height: 6), _buildDetailRow( + context, AppLocalizations.of( context, ).translate('work_order_v2.location'), @@ -104,6 +117,7 @@ class WorkOrderItemCard extends StatelessWidget { ], ] else if (workOrder.status == WorkOrderStatus.completed) ...[ _buildDetailRow( + context, AppLocalizations.of( context, ).translate('work_order_v2.complete_time'), @@ -113,6 +127,7 @@ class WorkOrderItemCard extends StatelessWidget { ), const SizedBox(height: 6), _buildDetailRow( + context, AppLocalizations.of( context, ).translate('work_order_v2.location'), @@ -148,23 +163,23 @@ class WorkOrderItemCard extends StatelessWidget { switch (workOrder.status) { case WorkOrderStatus.pending: - color = AppConstants.pendingColor; + color = context.appColors.warning; text = AppLocalizations.of(context).translate('work_order_v2.pending'); break; case WorkOrderStatus.executing: - color = AppConstants.executingColor; + color = context.appColors.primary; text = AppLocalizations.of( context, ).translate('work_order_v2.executing'); break; case WorkOrderStatus.completed: - color = AppConstants.completedColor; + color = context.appColors.success; text = AppLocalizations.of( context, ).translate('work_order_v2.completed'); break; default: - color = AppConstants.auxiliaryTextColor; + color = context.appColors.textTertiary; text = AppLocalizations.of(context).translate('common.unknown'); } @@ -196,7 +211,12 @@ class WorkOrderItemCard extends StatelessWidget { ); } - Widget _buildDetailRow(String label, String value, {Color? valueColor}) { + Widget _buildDetailRow( + BuildContext context, + String label, + String value, { + Color? valueColor, + }) { return Row( children: [ Flexible( @@ -204,7 +224,7 @@ class WorkOrderItemCard extends StatelessWidget { '$label:', style: TextStyle( fontSize: 13, - color: AppConstants.auxiliaryTextColor, + color: context.appColors.textTertiary, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -215,7 +235,7 @@ class WorkOrderItemCard extends StatelessWidget { value, style: TextStyle( fontSize: 13, - color: valueColor ?? AppConstants.secondaryTextColor, + color: valueColor ?? context.appColors.textSecondary, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -235,15 +255,15 @@ class WorkOrderItemCard extends StatelessWidget { '${AppLocalizations.of(context).translate('work_order_v2.progress')}:', style: TextStyle( fontSize: 13, - color: AppConstants.auxiliaryTextColor, + color: context.appColors.textTertiary, ), ), Expanded( child: LinearProgressIndicator( value: workOrder.progress! / 100, - backgroundColor: const Color(0xFFF0F0F0), + backgroundColor: context.appColors.divider, valueColor: AlwaysStoppedAnimation( - AppConstants.primaryColor, + context.appColors.primary, ), minHeight: 6, borderRadius: BorderRadius.circular(3), @@ -266,14 +286,14 @@ class WorkOrderItemCard extends StatelessWidget { } } - Color _getPriorityColor(WorkOrderPriority priority) { + Color _getPriorityColor(BuildContext context, WorkOrderPriority priority) { switch (priority) { case WorkOrderPriority.high: - return AppConstants.pendingColor; + return context.appColors.warning; case WorkOrderPriority.medium: - return AppConstants.pendingColor; + return context.appColors.warning; case WorkOrderPriority.low: - return AppConstants.auxiliaryTextColor; + return context.appColors.textTertiary; } } diff --git a/lib/features/v2/workorder/presentation/widgets/workorder_tabbar.dart b/lib/features/v2/workorder/presentation/widgets/workorder_tabbar.dart index 3a67c7f8..55d5abb6 100644 --- a/lib/features/v2/workorder/presentation/widgets/workorder_tabbar.dart +++ b/lib/features/v2/workorder/presentation/widgets/workorder_tabbar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:maibu_satabot_v2/core/localization/app_localizations.dart'; +import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import '../../../../../core/consts/workorder_consts.dart'; -import '../../domain/entities/workorder_entity.dart'; /// 工单标签栏组件 /// 展示待处理/执行中/已完成/全部四个静态标签 @@ -86,8 +86,8 @@ class WorkOrderTabBar extends StatelessWidget { fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, color: isSelected - ? AppConstants.primaryColor - : AppConstants.secondaryTextColor, + ? context.appColors.primary + : context.appColors.textSecondary, ), ), if (showBadge) @@ -111,7 +111,7 @@ class WorkOrderTabBar extends StatelessWidget { width: 24, height: 3, decoration: BoxDecoration( - color: AppConstants.primaryColor, + color: context.appColors.primary, borderRadius: BorderRadius.circular(1.5), ), ), diff --git a/lib/main.dart b/lib/main.dart index 10490243..e30c56fc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,6 +12,7 @@ import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart'; import 'package:maibu_satabot_v2/core/infrastructure/logging/app_bloc_observer.dart'; import 'package:maibu_satabot_v2/core/logging/i_logger_service.dart'; import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; +import 'package:maibu_satabot_v2/core/theme/theme_cubit.dart'; import 'package:maibu_satabot_v2/core/update/update_cubit.dart'; import 'package:maibu_satabot_v2/core/update/version_check_service.dart'; import 'package:maibu_satabot_v2/core/update/update_dialog.dart'; @@ -71,6 +72,7 @@ class MyApp extends StatelessWidget { sl().appStarted(); final deviceStatusBloc = sl(); final localeCubit = sl(); + final themeCubit = sl(); return MultiBlocProvider( providers: [ @@ -98,6 +100,7 @@ class MyApp extends StatelessWidget { ), BlocProvider.value(value: deviceStatusBloc), BlocProvider.value(value: localeCubit), + BlocProvider.value(value: themeCubit), BlocProvider.value(value: sl()), BlocProvider( create: (_) => sl(), @@ -113,20 +116,27 @@ class MyApp extends StatelessWidget { child: BlocBuilder( bloc: localeCubit, builder: (context, locale) { - return MaterialApp.router( - title: 'Maibu Satabot', - locale: locale, - supportedLocales: const [Locale('zh', 'CN'), Locale('en', 'US')], - localizationsDelegates: const [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - theme: AppTheme.lightTheme, - routerConfig: sl(), - builder: (context, child) { - return _LifecycleListener(child: _UpdateChecker(child: child!)); + return BlocBuilder( + bloc: themeCubit, + builder: (context, themeMode) { + return MaterialApp.router( + title: 'Maibu Satabot', + locale: locale, + supportedLocales: const [Locale('zh', 'CN'), Locale('en', 'US')], + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + theme: AppTheme.lightTheme, + darkTheme: AppTheme.darkTheme, + themeMode: themeMode, + routerConfig: sl(), + builder: (context, child) { + return _LifecycleListener(child: _UpdateChecker(child: child!)); + }, + ); }, ); }, diff --git a/pubspec.lock b/pubspec.lock index 8e83f20c..251afdf1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -984,10 +984,10 @@ packages: dependency: "direct main" description: name: mobile_scanner - sha256: ce3f059ebd6dbfab7292bba0e893e354b46730636820d3c9ef69005ce2d55bce + sha256: "1b60b8f9d4ce0cb0e7d7bc223c955d083a0737bee66fa1fcfe5de48225e0d5b3" url: "https://pub.flutter-io.cn" source: hosted - version: "7.4.0" + version: "3.5.7" mqtt_client: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 70f91f8b..6e9a56b9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -139,7 +139,7 @@ dependencies: markdown_widget: ^2.0.0 # 产品参数用的 markdown 渲染 flutter_markdown: ^0.7.1 - mobile_scanner: ^7.4.0 + mobile_scanner: ^3.4.1 vibration: ^3.1.8 flutter_patcher: ^0.1.2 # Add flutter_patcher here open_file: ^3.3.2 # 打开文件(安装 APK)