退出登陆的重新设计到我的页面;优化高清地图的加载和显示(冷启动稍慢,再次秒开);声网通用组件遗漏文件;
This commit is contained in:
@@ -141,6 +141,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
bool _isToastShowing = false;
|
||||
|
||||
final MapController _mapController = MapController();
|
||||
/// 全部场站高清叠加图层:初始用内置兜底,GetCapabilities 拉取成功后自动替换为动态范围
|
||||
List<TileLayer> _stationLayers = kAllStationTileLayers;
|
||||
// 初始化轨迹管理器(泛型指定为LatLng)
|
||||
late final TracePoint<PlotPoint> _traceManager;
|
||||
late String workMode = "覆盖模式"; // 作业模式:默认值为"覆盖模式"
|
||||
@@ -274,9 +276,24 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
}
|
||||
});
|
||||
|
||||
// 🌐 场站高清图层:监听动态范围更新 + 触发一次 GetCapabilities 刷新
|
||||
StationLayerRepo.instance.addListener(_onStationLayersUpdated);
|
||||
StationLayerRepo.instance.refresh();
|
||||
|
||||
// 🔄 进场刷新场站数据:避免 siteMapName/默认中心沿用上次缓存(无需退回首页重新请求接口)
|
||||
sl<SiteCubit>().reloadSites();
|
||||
|
||||
debugPrint('✅ [MapPage] initState 完成');
|
||||
}
|
||||
|
||||
/// GeoServer 动态范围拉取完成 → 用最新范围重建叠加图层
|
||||
void _onStationLayersUpdated() {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_stationLayers = StationLayerRepo.instance.buildLayers();
|
||||
});
|
||||
}
|
||||
|
||||
// 🔥 核心修复:监听路径规划应答流
|
||||
void _setupPathPlanningListener() {
|
||||
debugPrint('🔗 [MapPage] 开始监听路径规划指令应答 (CMD: 0x01)');
|
||||
@@ -1607,6 +1624,37 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
_mapController.move(_currentLatLng!, 17);
|
||||
}
|
||||
|
||||
/// 手动刷新高清场站图层(不动用户的作业/障碍物绘制数据):
|
||||
/// ① 清 WMS 瓦片缓存(拿后端最新影像)② 重拉场站接口(更新 siteMapName 映射)
|
||||
/// ③ 重拉 GetCapabilities(更新图层范围数组)④ 重建图层 ⑤ 定位到最新场站中心
|
||||
Future<void> _onRefreshStationLayers() async {
|
||||
debugPrint('🔄 [刷新图层] 开始手动刷新高清场站图层...');
|
||||
_showPageToast(message: '正在刷新高清图层...', type: ToastType.loading);
|
||||
try {
|
||||
await CachedWmsImage.clearTileCache();
|
||||
await sl<SiteCubit>().reloadSites();
|
||||
await StationLayerRepo.instance.refresh();
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_stationLayers = StationLayerRepo.instance.buildLayers();
|
||||
});
|
||||
final c = _getDefaultValidCenter();
|
||||
_mapController.move(c, 18);
|
||||
_cancelAllToast();
|
||||
_showPageToast(message: '已刷新到最新图层', type: ToastType.success);
|
||||
debugPrint('✅ [刷新图层] 完成,定位到 $c');
|
||||
} catch (e) {
|
||||
debugPrint('❌ [刷新图层] 失败: $e');
|
||||
if (!mounted) return;
|
||||
_cancelAllToast();
|
||||
final msg = e.toString();
|
||||
_showPageToast(
|
||||
message: '刷新失败: ${msg.length > 50 ? msg.substring(0, 50) : msg}',
|
||||
type: ToastType.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 障碍物模式:完成当前障碍物绘制 ==========
|
||||
void _completeObstacle() {
|
||||
debugPrint(
|
||||
@@ -1716,6 +1764,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
StationLayerRepo.instance.removeListener(_onStationLayersUpdated);
|
||||
_traceManager.reset();
|
||||
_mapController.dispose();
|
||||
_pathPlanningSub?.cancel(); // 🔥 取消订阅
|
||||
@@ -2178,10 +2227,10 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
),
|
||||
|
||||
/// GeoServer 全部场站无人机高清影像叠加层(WGS84 基准,无偏移)
|
||||
/// 一次性叠加所有场站图层:滑到哪个场站区域(zoom>=16)就自动显示哪个高清图
|
||||
/// 每层各自 tileBounds,框外瓦片不请求,开销极小;苏州等手动滑过去即可叠加
|
||||
/// 当前 WMS 兜底(原生 EPSG:4326);公司补配 GWC EPSG:3857 后改 geoserver_suzhou_layer.dart
|
||||
...kAllStationTileLayers,
|
||||
/// 图层范围优先取自 GeoServer GetCapabilities(动态),失败回退内置兜底
|
||||
/// 滑到哪个场站区域(zoom>=16)就自动显示哪个高清图;每层各自 tileBounds,框外不请求
|
||||
/// WMS 瓦片带磁盘缓存+超时(CachedWmsImage),二次进入秒开
|
||||
..._stationLayers,
|
||||
//if (isDjMapShow) DJITileLayer(stationName: _currentStation.name, mapController: _mapController),
|
||||
|
||||
// 绘制path折线(Line模式)
|
||||
@@ -4542,6 +4591,37 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
),
|
||||
),
|
||||
|
||||
/// 悬浮“刷新高清图层”按钮:后端改了图层/影像/场站映射后,
|
||||
/// 点它强制拉最新(清瓦片缓存 + 重拉场站接口 + 重拉 GetCapabilities + 重新定位)
|
||||
Positioned(
|
||||
top: 110,
|
||||
left: 10,
|
||||
child: GestureDetector(
|
||||
onTap: _onRefreshStationLayers,
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.8),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black12,
|
||||
blurRadius: 3,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: const Icon(
|
||||
Icons.refresh,
|
||||
color: Colors.black87,
|
||||
size: 22,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// 核心:地图正中心固定定位标
|
||||
if (_currentRobotMode == RobotMode.point)
|
||||
Positioned(
|
||||
@@ -4900,18 +4980,22 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
.deleteWorkRecord(
|
||||
deletedPlot.plotName,
|
||||
);
|
||||
final userId =
|
||||
context
|
||||
.read<AppUserCubit>()
|
||||
// 🔥 修复:删除后按“当前全局场站”重查,与打开列表(onListBox)走同一接口 selectBySiteId
|
||||
// 原逻辑用 loadWorkRecords(userId) 走 selectByUserId(按登录用户查),
|
||||
// 与列表口径不一致,导致删除后重查的数据不对;重新点列表才恢复正常。
|
||||
final selectedSite =
|
||||
sl<SiteCubit>()
|
||||
.state
|
||||
.user
|
||||
?.userId ??
|
||||
"";
|
||||
await context
|
||||
.read<DevicesCubit>()
|
||||
.loadWorkRecords(userId);
|
||||
.selectedSite;
|
||||
if (selectedSite != null) {
|
||||
await context
|
||||
.read<DevicesCubit>()
|
||||
.loadWorkRecordsBySiteId(
|
||||
selectedSite.id,
|
||||
);
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
if (mounted) setState(() {});
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
@@ -17,6 +17,7 @@ import 'package:maibu_satabot_v2/features/v2/home/presentation/widgets/power_tre
|
||||
import 'package:maibu_satabot_v2/features/v2/home/presentation/widgets/plant_overview_card.dart';
|
||||
import 'package:maibu_satabot_v2/features/v2/home/presentation/widgets/tcp_status_indicator.dart';
|
||||
import 'package:maibu_satabot_v2/components/device_status_modal.dart';
|
||||
import 'package:maibu_satabot_v2/components/agora_video_player.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_bloc.dart';
|
||||
|
||||
import '../../../../remote_control/presentation/bloc/remote_control_cubit.dart';
|
||||
@@ -31,6 +32,14 @@ class HomeV2Page extends StatefulWidget {
|
||||
class _HomeV2PageState extends State<HomeV2Page> {
|
||||
late HomeV2Bloc _bloc;
|
||||
|
||||
// ===== 电站实时视频(声网)默认测试配置 =====
|
||||
// 弹窗打开就用这组值自动开播,输入框仍保留,可随时改完点「重新加载」
|
||||
// TODO: 接口对接后改为后端下发的真实值
|
||||
static const String _agoraAppId = '875f6494d87548e783fefbf33f598c97';
|
||||
static const String _agoraChannel = '36test-01';
|
||||
static const String _agoraToken =
|
||||
'007eJxTYLhsYtaz3e2gTMKZ27JzrOs/KB3OTFaYKfGX2+/Y6zem5TMUGCzMTdPMTCxNUoAME4tUcwvjtNS0pDRj4zRTS4tkS3P55PlZDYGMDGsfyTEyMkAgiM/JYGxWklpcomtgyMAAAN9rIEI=';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -203,6 +212,7 @@ class _HomeV2PageState extends State<HomeV2Page> {
|
||||
const SizedBox(height: 10),
|
||||
PlantOverviewCard(
|
||||
overview: state.homeData.plantOverview,
|
||||
onTap: _showPlantVideo,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
WorkOrderCard(stats: state.homeData.workOrderStats),
|
||||
@@ -247,6 +257,19 @@ class _HomeV2PageState extends State<HomeV2Page> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 点击「电站概览」卡片 → 弹出声网视频组件
|
||||
///
|
||||
/// 默认带上测试配置自动开播,同时保留三个输入框可手动修改
|
||||
void _showPlantVideo() {
|
||||
AgoraVideoPlayer.showManualInput(
|
||||
context,
|
||||
title: '电站实时视频',
|
||||
initialAppId: _agoraAppId,
|
||||
initialChannelId: _agoraChannel,
|
||||
initialToken: _agoraToken,
|
||||
);
|
||||
}
|
||||
|
||||
void _showPlantSelector() {
|
||||
if (_bloc.state is! HomeV2Loaded) return;
|
||||
|
||||
|
||||
@@ -6,10 +6,21 @@ import 'package:maibu_satabot_v2/features/v2/home/domain/entities/home_entity.da
|
||||
class PlantOverviewCard extends StatelessWidget {
|
||||
final PlantOverview overview;
|
||||
|
||||
const PlantOverviewCard({super.key, required this.overview});
|
||||
/// 点击整张卡片的回调(如弹出电站实时视频)
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const PlantOverviewCard({super.key, required this.overview, this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: _buildContent(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
@@ -53,12 +53,13 @@ class MyRemoteDataSourceImpl implements MyRemoteDataSource {
|
||||
title: '帮助与反馈',
|
||||
icon: 'help',
|
||||
),
|
||||
MenuItemModel(
|
||||
id: 'about_us',
|
||||
title: '关于我们',
|
||||
icon: 'info',
|
||||
subTitle: 'V1.0.1',
|
||||
),
|
||||
// 关于我们(已注释掉,其位置由“退出登录”占用)
|
||||
// MenuItemModel(
|
||||
// id: 'about_us',
|
||||
// title: '关于我们',
|
||||
// icon: 'info',
|
||||
// subTitle: 'V1.0.1',
|
||||
// ),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get_it/get_it.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/localization/app_localizations.dart';
|
||||
import 'package:maibu_satabot_v2/core/router/route_paths.dart';
|
||||
import 'package:maibu_satabot_v2/core/storage/user_storage.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/auth/presentation/bloc/auth_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';
|
||||
@@ -125,8 +130,13 @@ class _MyPageContent extends StatelessWidget {
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: state.menuItems.length,
|
||||
// 末尾追加退出登录,占据“关于我们”原来的位置
|
||||
itemCount: state.menuItems.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
// 退出登录(列表最后一项,在可滚动区域内避免底部溢出)
|
||||
if (index == state.menuItems.length) {
|
||||
return _buildLogoutButton(context);
|
||||
}
|
||||
final item = state.menuItems[index];
|
||||
// 🔥 夜间模式开关值以全局 ThemeCubit 状态为准
|
||||
final displayItem = item.id == 'night_mode'
|
||||
@@ -221,4 +231,61 @@ class _MyPageContent extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 退出登录(作为菜单列表最后一项,占据“关于我们”原来的位置)
|
||||
Widget _buildLogoutButton(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_showLogoutDialog(context);
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: SizedBox(
|
||||
height: 56,
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalizations.of(context).translate('login.logout'),
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFFF53F3F),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 显示退出登录确认对话框
|
||||
void _showLogoutDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context).translate('login.logout')),
|
||||
content: Text(
|
||||
AppLocalizations.of(context).translate('login.confirm_logout'),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(AppLocalizations.of(context).translate('my_v2.cancel')),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
// 执行退出登录逻辑
|
||||
await GetIt.I<UserStorage>().deleteUser();
|
||||
GetIt.I<AuthCubit>().logout();
|
||||
context.go(RoutePaths.login);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: const Color(0xFFF53F3F),
|
||||
),
|
||||
child: Text(
|
||||
AppLocalizations.of(context).translate('my_v2.confirm'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,9 +77,9 @@ class _SystemSettingsPageState extends State<SystemSettingsPage> {
|
||||
// 版本更新
|
||||
_buildUpdateSection(),
|
||||
const SizedBox(height: 12),
|
||||
// 退出登录
|
||||
_buildLogoutButton(context),
|
||||
const SizedBox(height: 20),
|
||||
// 退出登录(已挪至“我的”页面最下边,此处注释掉)
|
||||
// _buildLogoutButton(context),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -421,70 +421,70 @@ class _SystemSettingsPageState extends State<SystemSettingsPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 退出登录按钮
|
||||
Widget _buildLogoutButton(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_showLogoutDialog(context);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: context.appColors.cardBackground,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.appColors.cardShadow,
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalizations.of(context).translate('login.logout'),
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFFF53F3F),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
/// 退出登录按钮(已挪至“我的”页面最下边,此处注释掉)
|
||||
// Widget _buildLogoutButton(BuildContext context) {
|
||||
// return GestureDetector(
|
||||
// onTap: () {
|
||||
// _showLogoutDialog(context);
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
// decoration: BoxDecoration(
|
||||
// color: context.appColors.cardBackground,
|
||||
// borderRadius: BorderRadius.circular(12),
|
||||
// boxShadow: [
|
||||
// BoxShadow(
|
||||
// color: context.appColors.cardShadow,
|
||||
// blurRadius: 8,
|
||||
// offset: Offset(0, 2),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// child: Center(
|
||||
// child: Text(
|
||||
// AppLocalizations.of(context).translate('login.logout'),
|
||||
// style: const TextStyle(
|
||||
// fontSize: 16,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Color(0xFFF53F3F),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
/// 显示退出登录确认对话框
|
||||
void _showLogoutDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context).translate('login.logout')),
|
||||
content: Text(
|
||||
AppLocalizations.of(context).translate('login.confirm_logout'),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(AppLocalizations.of(context).translate('my_v2.cancel')),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
// 执行退出登录逻辑
|
||||
await GetIt.I<UserStorage>().deleteUser();
|
||||
GetIt.I<AuthCubit>().logout();
|
||||
context.go(RoutePaths.login);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: const Color(0xFFF53F3F),
|
||||
),
|
||||
child: Text(
|
||||
AppLocalizations.of(context).translate('my_v2.confirm'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
/// 显示退出登录确认对话框(已挪至“我的”页面最下边,此处注释掉)
|
||||
// void _showLogoutDialog(BuildContext context) {
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (context) => AlertDialog(
|
||||
// title: Text(AppLocalizations.of(context).translate('login.logout')),
|
||||
// content: Text(
|
||||
// AppLocalizations.of(context).translate('login.confirm_logout'),
|
||||
// ),
|
||||
// actions: [
|
||||
// TextButton(
|
||||
// onPressed: () => Navigator.pop(context),
|
||||
// child: Text(AppLocalizations.of(context).translate('my_v2.cancel')),
|
||||
// ),
|
||||
// TextButton(
|
||||
// onPressed: () async {
|
||||
// Navigator.pop(context);
|
||||
// // 执行退出登录逻辑
|
||||
// await GetIt.I<UserStorage>().deleteUser();
|
||||
// GetIt.I<AuthCubit>().logout();
|
||||
// context.go(RoutePaths.login);
|
||||
// },
|
||||
// style: TextButton.styleFrom(
|
||||
// foregroundColor: const Color(0xFFF53F3F),
|
||||
// ),
|
||||
// child: Text(
|
||||
// AppLocalizations.of(context).translate('my_v2.confirm'),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user