From c8b305a4f240eaa21ea2a7b3d0ff6d5b766e92af Mon Sep 17 00:00:00 2001 From: liyi Date: Tue, 21 Jan 2025 10:24:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E8=A7=86=E9=A2=91=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=80=BB=E8=BE=91=E3=80=81=E8=A7=86=E9=A2=91?= =?UTF-8?q?/=E5=9B=BE=E7=89=87=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gradle/wrapper/gradle-wrapper.properties | 3 +- lan/lan_en.json | 1 + lan/lan_keys.json | 1 + lan/lan_zh.json | 1 + .../doorLockLog/doorLockLog_page.dart | 28 +- .../editVideoLog/editVideoLog_logic.dart | 71 +++- .../editVideoLog/editVideoLog_page.dart | 391 ++++++++++++++---- .../videoLog/videoLog/videoLog_entity.dart | 26 +- .../videoLog/videoLog/videoLog_logic.dart | 43 +- .../videoLog/videoLog/videoLog_page.dart | 43 +- .../videoLogDetail/controlsOverlay_page.dart | 7 +- .../videoLogDetail/videoLogDetail_page.dart | 75 ++-- .../widget/full_screenImage_page.dart | 14 +- .../videoLog/widget/video_thumbnail.dart | 19 +- 14 files changed, 540 insertions(+), 183 deletions(-) diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index cb24abda..0971a052 100755 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +#distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-7.4-all.zip diff --git a/lan/lan_en.json b/lan/lan_en.json index e07a619d..6d57fbc0 100644 --- a/lan/lan_en.json +++ b/lan/lan_en.json @@ -1126,6 +1126,7 @@ "通话连接失败": "Call connection failed", "已挂断": "Hanging up", "正在说话...": "Talking now...", + "下载完成,请到相册查看": "Download completed, please go to the album to view", "重置后,该锁的遥控都将被删除哦,确认要重置吗?": "After reset, the remote control of the lock will be deleted. Do you want to reset it?", "版本说明": "Version description" } diff --git a/lan/lan_keys.json b/lan/lan_keys.json index dbeaa4d8..d99c1b05 100755 --- a/lan/lan_keys.json +++ b/lan/lan_keys.json @@ -1126,6 +1126,7 @@ "通话连接失败": "通话连接失败", "已挂断": "已挂断", "正在说话...": "正在说话...", + "下载完成,请到相册查看": "下载完成,请到相册查看", "重置后,该锁的遥控都将被删除哦,确认要重置吗?": "重置后,该锁的遥控都将被删除哦,确认要重置吗?", "版本说明": "版本说明" } diff --git a/lan/lan_zh.json b/lan/lan_zh.json index 32abc2e1..e26b13ce 100755 --- a/lan/lan_zh.json +++ b/lan/lan_zh.json @@ -1126,6 +1126,7 @@ "通话连接失败": "通话连接失败", "已挂断": "已挂断", "正在说话...": "正在说话...", + "下载完成,请到相册查看": "下载完成,请到相册查看", "重置后,该锁的遥控都将被删除哦,确认要重置吗?": "重置后,该锁的遥控都将被删除哦,确认要重置吗?", "版本说明": "版本说明" } diff --git a/lib/main/lockDetail/doorLockLog/doorLockLog_page.dart b/lib/main/lockDetail/doorLockLog/doorLockLog_page.dart index 3634ae4e..fca6b402 100755 --- a/lib/main/lockDetail/doorLockLog/doorLockLog_page.dart +++ b/lib/main/lockDetail/doorLockLog/doorLockLog_page.dart @@ -403,17 +403,23 @@ class _DoorLockLogPageState extends State with RouteAware { } _buildImageItem(RecordListData recordData) { - return Image.network( - recordData.imagesUrl!, - fit: BoxFit.cover, - errorBuilder: - (BuildContext context, Object error, StackTrace? stackTrace) { - // 图片加载失败时显示错误图片 - return Image.asset( - 'images/icon_unHaveData.png', // 错误图片路径 - fit: BoxFit.cover, - ); - }, + return RotatedBox( + quarterTurns: -1, + child: Image.network( + recordData.imagesUrl!, + fit: BoxFit.cover, + errorBuilder: + (BuildContext context, Object error, StackTrace? stackTrace) { + // 图片加载失败时显示错误图片 + return RotatedBox( + quarterTurns: -1, + child: Image.asset( + 'images/icon_unHaveData.png', // 错误图片路径 + fit: BoxFit.cover, + ), + ); + }, + ), ); } diff --git a/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_logic.dart b/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_logic.dart index f6d1dacd..8ad36896 100755 --- a/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_logic.dart +++ b/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_logic.dart @@ -1,4 +1,10 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; import 'package:get/get.dart'; +import 'package:image_gallery_saver/image_gallery_saver.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart'; import 'package:star_lock/network/api_repository.dart'; import 'package:star_lock/tools/baseGetXController.dart'; @@ -9,18 +15,20 @@ class EditVideoLogLogic extends BaseGetXController { EditVideoLogState state = EditVideoLogState(); Future deleteLockCloudStorageList() async { - final VersionUndateEntity entity = await ApiRepository.to.deleteLockCloudStorageList( + final VersionUndateEntity entity = + await ApiRepository.to.deleteLockCloudStorageList( recordIds: state.selectVideoLogList.value.map((e) => e.recordId).toList(), ); if (entity.errorCode!.codeIsSuccessful) { - state.selectVideoLogList.value.clear(); showToast('删除成功'.tr); - getLockCloudStorageList(); + await getLockCloudStorageList(); + state.selectVideoLogList.clear(); } } Future getLockCloudStorageList() async { - final VideoLogEntity entity = await ApiRepository.to.getLockCloudStorageList( + final VideoLogEntity entity = + await ApiRepository.to.getLockCloudStorageList( lockId: state.getLockId.value, ); if (entity.errorCode!.codeIsSuccessful) { @@ -28,4 +36,59 @@ class EditVideoLogLogic extends BaseGetXController { state.videoLogList.refresh(); } } + + Future downloadAndSaveToGallery(String url, String fileName) async { + try { + // 请求存储权限 + if (await _requestPermission()) { + final dio = Dio(); + final directory = await getTemporaryDirectory(); + final filePath = '${directory.path}/$fileName'; + + // 下载文件 + await dio.download( + url, + filePath, + onReceiveProgress: (received, total) { + if (total != -1) { + final progress = (received / total) * 100; + print('下载进度: $progress%'); + } + }, + ); + + // 保存到图库 + if (fileName.endsWith('.jpg') || fileName.endsWith('.png')) { + // 保存图片 + final result = await ImageGallerySaver.saveFile(filePath); + if (result['isSuccess']) { + print('图片已保存到图库'); + } else { + print('图片保存失败'); + } + } else if (fileName.endsWith('.mp4')) { + // 保存视频 + final result = await ImageGallerySaver.saveFile(filePath); + if (result['isSuccess']) { + print('视频已保存到图库'); + } else { + print('视频保存失败'); + } + } + + // 删除临时文件 + await File(filePath).delete(); + } else { + print('存储权限被拒绝'); + } + } catch (e) { + print('下载或保存文件失败: $e'); + } + } + +// 请求存储权限 + Future _requestPermission() async { + final status = await Permission.storage.request(); + return status.isGranted; + } } diff --git a/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_page.dart b/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_page.dart index d7957341..c52a90d5 100755 --- a/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_page.dart +++ b/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_page.dart @@ -1,10 +1,12 @@ - import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:star_lock/appRouters.dart'; import 'package:star_lock/main/lockDetail/videoLog/editVideoLog/editVideoLog_state.dart'; import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart'; +import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart'; +import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail.dart'; import 'package:star_lock/tools/dateTool.dart'; import '../../../../app_settings/app_colors.dart'; @@ -97,6 +99,7 @@ class _EditVideoLogPageState extends State { double itemW = (1.sw - 15.w * 4) / 3; double itemH = (1.sw - 15.w * 4) / 3 + 40.h; + Widget mainListView(int index, CloudStorageData itemData) { return GridView.builder( padding: EdgeInsets.only(left: 15.w, right: 15.w), @@ -114,116 +117,322 @@ class _EditVideoLogPageState extends State { childAspectRatio: itemW / itemH), itemBuilder: (BuildContext context, int index) { final RecordListData recordData = itemData.recordList![index]; - return videoItem(recordData, index); + return videoItem(recordData); }, ); } - Widget videoItem(RecordListData recordData, int index) { - return Container( - width: itemW, - height: itemH, - color: Colors.white, - child: GestureDetector( - onTap: () { - recordData.isSelect = !recordData.isSelect!; - if (recordData.isSelect! == true) { - state.selectVideoLogList.add(recordData); - } else { - state.selectVideoLogList.remove(recordData); - } - setState(() {}); - }, - child: Stack( - children: [ - Column( - children: [ - Container( - width: itemW, - height: itemW, - margin: const EdgeInsets.all(0), - color: Colors.white, - child: ClipRRect( - borderRadius: BorderRadius.circular(10.w), - child: Image( - fit: BoxFit.cover, - image: Image.network(recordData.imagesUrl ?? - 'images/icon_video_placeholder.jpg') - .image), - ), - ), - SizedBox(height: 5.h), - Text( - DateTool() - .dateToYMDHNString(recordData.operateDate.toString()), - textAlign: TextAlign.center, - style: TextStyle(fontSize: 18.sp)) - ], - ), - Positioned( - top: 0.w, - right: 0.w, - child: Image( - width: 36.w, - height: 36.w, - image: state.selectVideoLogList.value.contains(recordData) - ? const AssetImage('images/icon_round_select.png') - : const AssetImage('images/icon_round_unSelect.png'))) - ], - )), - ); - } + // Widget videoItem(RecordListData recordData, int index) { + // return Container( + // width: itemW, + // height: itemH, + // color: Colors.white, + // child: GestureDetector( + // onTap: () { + // recordData.isSelect = !recordData.isSelect!; + // if (recordData.isSelect! == true) { + // state.selectVideoLogList.add(recordData); + // } else { + // state.selectVideoLogList.remove(recordData); + // } + // setState(() {}); + // }, + // child: Stack( + // children: [ + // Column( + // children: [ + // Container( + // width: itemW, + // height: itemW, + // margin: const EdgeInsets.all(0), + // color: Colors.white, + // child: ClipRRect( + // borderRadius: BorderRadius.circular(10.w), + // child: Image( + // fit: BoxFit.cover, + // image: Image.network(recordData.imagesUrl ?? + // 'images/icon_video_placeholder.jpg') + // .image), + // ), + // ), + // SizedBox(height: 5.h), + // Text( + // DateTool() + // .dateToYMDHNString(recordData.operateDate.toString()), + // textAlign: TextAlign.center, + // style: TextStyle(fontSize: 18.sp)) + // ], + // ), + // Positioned( + // top: 0.w, + // right: 0.w, + // child: Image( + // width: 36.w, + // height: 36.w, + // image: state.selectVideoLogList.value.contains(recordData) + // ? const AssetImage('images/icon_round_select.png') + // : const AssetImage('images/icon_round_unSelect.png'))) + // ], + // )), + // ); + // } Widget bottomBottomBtnWidget() { return SizedBox( width: 1.sw, - child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + child: + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ bottomBtnItemWidget( - 'images/main/icon_lockDetail_monitoringDownloadVideo.png', - '下载'.tr, - Colors.white, () { - if (state.selectVideoLogList.value.isNotEmpty) { - Get.toNamed(Routers.videoLogDownLoadPage, arguments: >{ - 'downloadVideoLogList': state.selectVideoLogList.value - }); - } else { - logic.showToast('请选择要下载的视频'); - } - }), + 'images/main/icon_lockDetail_monitoringDownloadVideo.png', + '下载'.tr, + Colors.white, + _onDownLoadClick, + ), SizedBox(width: 100.w), bottomBtnItemWidget( - 'images/main/icon_lockDetail_monitoringDeletVideo.png', - '删除'.tr, - AppColors.mainColor, () { - if (state.selectVideoLogList.value.isNotEmpty) { - logic.deleteLockCloudStorageList(); - } else { - logic.showToast('请选择要删除的视频'.tr); - } - }) + 'images/main/icon_lockDetail_monitoringDeletVideo.png', + '删除'.tr, + AppColors.mainColor, + _onDelClick, + ) ]), ); } + Future _onDownLoadClick() async { + if (state.selectVideoLogList.value.isNotEmpty) { + double _progress = 0.0; + // 开始下载 + // 显示进度条 + EasyLoading.showProgress(_progress, status: '加载数据中'.tr); + + // 模拟进度更新 + for (int i = 0; i <= state.selectVideoLogList.length - 1; i++) { + final item = state.selectVideoLogList.value[i]; + + // 判断 imagesUrl 是否为空 + if (item.imagesUrl != null && item.imagesUrl!.isNotEmpty) { + await logic.downloadAndSaveToGallery(item.imagesUrl!, 'image_$i.jpg'); + } + + // 判断 videoUrl 是否为空 + if (item.videoUrl != null && item.videoUrl!.isNotEmpty) { + await logic.downloadAndSaveToGallery(item.videoUrl!, 'video_$i.mp4'); + } + + // 更新进度 + _progress = (i + 1) / state.selectVideoLogList.length; + EasyLoading.showProgress(_progress, status: '加载数据中'.tr); + } + + // 加载完成后隐藏进度条 + EasyLoading.dismiss(); + EasyLoading.showSuccess('下载完成,请到相册查看'.tr); + state.selectVideoLogList.clear(); + setState(() {}); + // Get.toNamed(Routers.videoLogDownLoadPage, + // arguments: >{ + // 'downloadVideoLogList': state.selectVideoLogList.value + // }); + } else { + logic.showToast('请选择要下载的视频'); + } + } + + Future _onDelClick() async { + if (state.selectVideoLogList.value.isNotEmpty) { + // 弹出自定义确认对话框 + bool confirmDelete = await showDialog( + context: context, + builder: (BuildContext context) { + return Dialog( + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '确认删除'.tr, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), + Text('确定要删除选中的视频吗?'.tr), + SizedBox(height: 24), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + // 用户点击取消,返回 false + Navigator.of(context).pop(false); + }, + child: Text('取消'.tr), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () { + // 用户点击确认,返回 true + Navigator.of(context).pop(true); + }, + child: Text('确认'.tr), + ), + ], + ), + ], + ), + ), + ); + }, + ); + + // 如果用户确认删除,执行删除逻辑 + if (confirmDelete == true) { + await logic.deleteLockCloudStorageList(); + } + } else { + logic.showToast('请选择要删除的视频'.tr); + } + } + Widget bottomBtnItemWidget( - String iconUrl, String name, Color backgroundColor, Function() onClick) { + String iconUrl, + String name, + Color backgroundColor, + Future Function() onClick, + ) { final double wh = 40.w; return GestureDetector( onTap: onClick, child: SizedBox( - height: 140.h, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(height: 30.w), - Image.asset(iconUrl, width: wh, height: wh, fit: BoxFit.fitWidth), - SizedBox(height: 10.w), - Expanded( - child: Text(name, - style: TextStyle(fontSize: 22.sp), - textAlign: TextAlign.center)) - ], - )), + height: 140.h, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 30.w), + Image.asset(iconUrl, width: wh, height: wh, fit: BoxFit.fitWidth), + SizedBox(height: 10.w), + Expanded( + child: Text(name, + style: TextStyle(fontSize: 22.sp), + textAlign: TextAlign.center), + ) + ], + ), + ), + ); + } + + Widget videoItem(RecordListData recordData) { + return GestureDetector( + onTap: () { + if (recordData.videoUrl != null && recordData.videoUrl!.isNotEmpty) { + Get.toNamed(Routers.videoLogDetailPage, arguments: { + 'recordData': recordData, + 'videoDataList': state.videoLogList.value + }); + } else if (recordData.imagesUrl != null && + recordData.imagesUrl!.isNotEmpty) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => FullScreenImagePage( + imageUrl: recordData.imagesUrl!, + ), + ), + ); + } + }, + child: Stack( + children: [ + SizedBox( + width: itemW, + height: itemH, + child: Column( + children: [ + Container( + width: itemW, + height: itemW, + margin: const EdgeInsets.all(0), + color: Colors.white, + child: ClipRRect( + borderRadius: BorderRadius.circular(10.w), + child: _buildImageOrVideoItem(recordData), + ), + ), + SizedBox(height: 5.h), + Text( + DateTool() + .dateToYMDHNString(recordData.operateDate.toString()), + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18.sp), + ) + ], + ), + ), + Positioned( + top: 0.w, + right: 0.w, + child: GestureDetector( + onTap: () { + recordData.isSelect = !recordData.isSelect!; + if (recordData.isSelect! == true) { + state.selectVideoLogList.add(recordData); + } else { + state.selectVideoLogList.remove(recordData); + } + setState(() {}); + }, + child: Image( + width: 36.w, + height: 36.w, + image: state.selectVideoLogList.value.contains(recordData) + ? const AssetImage('images/icon_round_select.png') + : const AssetImage('images/icon_round_unSelect.png'), + ), + ), + ) + ], + ), + ); + } + + _buildImageOrVideoItem(RecordListData recordData) { + if (recordData.videoUrl != null && recordData.videoUrl!.isNotEmpty) { + return _buildVideoItem(recordData); + } else { + return _buildImageItem(recordData); + } + } + + _buildVideoItem(RecordListData recordData) { + return VideoThumbnail(videoUrl: recordData.videoUrl!); + } + + _buildImageItem(RecordListData recordData) { + return RotatedBox( + quarterTurns: -1, + child: Image.network( + recordData.imagesUrl!, + fit: BoxFit.cover, + errorBuilder: + (BuildContext context, Object error, StackTrace? stackTrace) { + // 图片加载失败时显示错误图片 + return RotatedBox( + quarterTurns: -1, + child: Image.asset( + 'images/icon_unHaveData.png', // 错误图片路径 + fit: BoxFit.cover, + ), + ); + }, + ), ); } } diff --git a/lib/main/lockDetail/videoLog/videoLog/videoLog_entity.dart b/lib/main/lockDetail/videoLog/videoLog/videoLog_entity.dart index 9e8dd9a2..17c9514b 100755 --- a/lib/main/lockDetail/videoLog/videoLog/videoLog_entity.dart +++ b/lib/main/lockDetail/videoLog/videoLog/videoLog_entity.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; +import 'dart:typed_data'; + class VideoLogEntity { int? errorCode; String? description; @@ -61,21 +64,28 @@ class RecordListData { int? operateDate; String? imagesUrl; String? videoUrl; + Uint8List? thumbnailData; // 将视频封面改为 Uint8List 类型 int? recordType; bool? isSelect = false; - RecordListData( - {this.recordId, - this.operateDate, - this.imagesUrl, - this.videoUrl, - this.recordType}); + RecordListData({ + this.recordId, + this.operateDate, + this.imagesUrl, + this.videoUrl, + this.thumbnailData, // 视频封面数据 + this.recordType, + }); RecordListData.fromJson(Map json) { recordId = json['recordId']; operateDate = json['operateDate']; imagesUrl = json['imagesUrl']; videoUrl = json['videoUrl']; + // 如果 JSON 中包含 base64 编码的图片数据,可以解码为 Uint8List + if (json['thumbnailData'] != null) { + thumbnailData = base64Decode(json['thumbnailData']); + } recordType = json['recordType']; } @@ -85,6 +95,10 @@ class RecordListData { data['operateDate'] = operateDate; data['imagesUrl'] = imagesUrl; data['videoUrl'] = videoUrl; + // 将 Uint8List 编码为 base64 字符串 + if (thumbnailData != null) { + data['thumbnailData'] = base64Encode(thumbnailData!); + } data['recordType'] = recordType; return data; } diff --git a/lib/main/lockDetail/videoLog/videoLog/videoLog_logic.dart b/lib/main/lockDetail/videoLog/videoLog/videoLog_logic.dart index 763b8d54..41124660 100755 --- a/lib/main/lockDetail/videoLog/videoLog/videoLog_logic.dart +++ b/lib/main/lockDetail/videoLog/videoLog/videoLog_logic.dart @@ -1,5 +1,6 @@ import 'dart:typed_data'; +import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart'; import 'package:star_lock/network/api_repository.dart'; import 'package:star_lock/tools/baseGetXController.dart'; @@ -14,19 +15,45 @@ class VideoLogLogic extends BaseGetXController { ); if (entity.errorCode!.codeIsSuccessful) { state.videoLogList.value = entity.data!; - // state.videoLogList.value.forEach((element) { - // // 过滤掉 imagesUrl 和 videoUrl 都为 null 或空字符串的项 - // element.recordList = element.recordList! - // .where((record) => - // (record.imagesUrl != null && record.imagesUrl!.isNotEmpty) || - // (record.videoUrl != null && record.videoUrl!.isNotEmpty)) - // .toList(); - // }); + state.videoLogList.value.forEach((element) { + // 过滤掉 imagesUrl 和 videoUrl 都为 null 或空字符串的项 + element.recordList = element.recordList! + .where((record) => + (record.imagesUrl != null && record.imagesUrl!.isNotEmpty) || + (record.videoUrl != null && record.videoUrl!.isNotEmpty)) + .toList(); + + // // 为 videoUrl 不为空的项设置封面 + // for (var record in element.recordList!) { + // if (record.videoUrl != null && record.videoUrl!.isNotEmpty) { + // _setVideoThumbnail(record); // 设置视频封面 + // } + // } + }); state.videoLogList.refresh(); } } + // Future _setVideoThumbnail(RecordListData record) async { + // try { + // final thumbnailData = await getVideoThumbnail(record.videoUrl!); + // record.thumbnailData = thumbnailData; // 设置视频封面数据 + // } catch (e) { + // print('获取视频封面失败: $e'); + // } + // } + // + // Future getVideoThumbnail(String videoUrl) async { + // final thumbnail = await VideoThumbnail.thumbnailData( + // video: videoUrl, + // imageFormat: ImageFormat.JPEG, + // maxWidth: 128, // 缩略图的最大宽度 + // quality: 25, // 缩略图的质量(0-100) + // ); + // return thumbnail; + // } + @override onReady() { super.onReady(); diff --git a/lib/main/lockDetail/videoLog/videoLog/videoLog_page.dart b/lib/main/lockDetail/videoLog/videoLog/videoLog_page.dart index 992243f0..994f7383 100755 --- a/lib/main/lockDetail/videoLog/videoLog/videoLog_page.dart +++ b/lib/main/lockDetail/videoLog/videoLog/videoLog_page.dart @@ -228,15 +228,18 @@ class _VideoLogPageState extends State { child: Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(state.isNavLocal.value == true ? '已下载'.tr : '全部视频'.tr, - style: TextStyle(fontSize: 26.sp, fontWeight: FontWeight.w500)), + Text( + state.isNavLocal.value == true ? '已下载'.tr : '全部视频'.tr, + style: TextStyle(fontSize: 26.sp, fontWeight: FontWeight.w500), + ), Expanded(child: SizedBox(width: 10.w)), IconButton( icon: Image( - width: 40.w, - height: 40.w, - image: const AssetImage( - 'images/main/icon_lockDetail_monitoringEditVoice.png')), + width: 40.w, + height: 40.w, + image: const AssetImage( + 'images/main/icon_lockDetail_monitoringEditVoice.png'), + ), iconSize: 30, color: Colors.black54, onPressed: () { @@ -342,17 +345,23 @@ class _VideoLogPageState extends State { } _buildImageItem(RecordListData recordData) { - return Image.network( - recordData.imagesUrl!, - fit: BoxFit.cover, - errorBuilder: - (BuildContext context, Object error, StackTrace? stackTrace) { - // 图片加载失败时显示错误图片 - return Image.asset( - 'images/icon_unHaveData.png', // 错误图片路径 - fit: BoxFit.cover, - ); - }, + return RotatedBox( + quarterTurns: -1, + child: Image.network( + recordData.imagesUrl!, + fit: BoxFit.cover, + errorBuilder: + (BuildContext context, Object error, StackTrace? stackTrace) { + // 图片加载失败时显示错误图片 + return RotatedBox( + quarterTurns: -1, + child: Image.asset( + 'images/icon_unHaveData.png', // 错误图片路径 + fit: BoxFit.cover, + ), + ); + }, + ), ); } } diff --git a/lib/main/lockDetail/videoLog/videoLogDetail/controlsOverlay_page.dart b/lib/main/lockDetail/videoLog/videoLogDetail/controlsOverlay_page.dart index 665aed93..c90cc379 100755 --- a/lib/main/lockDetail/videoLog/videoLogDetail/controlsOverlay_page.dart +++ b/lib/main/lockDetail/videoLog/videoLogDetail/controlsOverlay_page.dart @@ -148,9 +148,10 @@ class _ControlsOverlayState extends State { // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - DateTool().dateToYMDHNString( - widget.recordData.operateDate.toString()), - style: TextStyle(color: Colors.white, fontSize: 20.sp)), + DateTool().dateToYMDHNString( + widget.recordData.operateDate.toString()), + style: TextStyle(color: Colors.white, fontSize: 20.sp), + ), // Expanded(child: SizedBox(width: 10.w)), // Container( // width: 50.w, diff --git a/lib/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_page.dart b/lib/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_page.dart index 16e4c12f..bd680cab 100755 --- a/lib/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_page.dart +++ b/lib/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; @@ -72,37 +73,35 @@ class _VideoLogDetailPageState extends State { ? Column( children: [ Container( + color: Colors.black, height: 500.h, - decoration: BoxDecoration(color: Colors.black), - child: AspectRatio( - aspectRatio: 16 / 9, - child: Stack( - alignment: Alignment.bottomCenter, - children: [ - FittedBox( - child: SizedBox( - width: state.videoController.value.size.width, - height: state.videoController.value.size.height, - child: VideoPlayer(state.videoController), - ), - fit: BoxFit.cover, + width: 1.sw, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + RotatedBox( + quarterTurns: -1, + child: AspectRatio( + aspectRatio: state.videoController.value.size.width / + state.videoController.value.size.height, + child: VideoPlayer(state.videoController), ), - ControlsOverlay( - controller: state.videoController, - recordData: state.recordData.value, + ), + ControlsOverlay( + controller: state.videoController, + recordData: state.recordData.value, + ), + if (state.videoController.value.isPlaying || + state.videoController.value.isBuffering) + Container() + else + VideoProgressIndicator( + state.videoController, + colors: VideoProgressColors( + playedColor: AppColors.mainColor), + allowScrubbing: true, ), - if (state.videoController.value.isPlaying || - state.videoController.value.isBuffering) - Container() - else - VideoProgressIndicator( - state.videoController, - colors: VideoProgressColors( - playedColor: AppColors.mainColor), - allowScrubbing: true, - ), - ], - ), + ], ), ), _buildOther(), @@ -170,9 +169,23 @@ class _VideoLogDetailPageState extends State { } _buildImageItem(RecordListData recordData) { - return Image.network( - recordData.imagesUrl!, - fit: BoxFit.cover, + return RotatedBox( + quarterTurns: 1, + child: Image.network( + recordData.imagesUrl!, + fit: BoxFit.cover, + errorBuilder: + (BuildContext context, Object error, StackTrace? stackTrace) { + // 图片加载失败时显示错误图片 + return RotatedBox( + quarterTurns: -1, + child: Image.asset( + 'images/icon_unHaveData.png', // 错误图片路径 + fit: BoxFit.cover, + ), + ); + }, + ), ); } diff --git a/lib/main/lockDetail/videoLog/widget/full_screenImage_page.dart b/lib/main/lockDetail/videoLog/widget/full_screenImage_page.dart index 9c516439..f2da5bf9 100644 --- a/lib/main/lockDetail/videoLog/widget/full_screenImage_page.dart +++ b/lib/main/lockDetail/videoLog/widget/full_screenImage_page.dart @@ -10,9 +10,17 @@ class FullScreenImagePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - body: Container( - child: PhotoView( - imageProvider: NetworkImage(imageUrl), + body: GestureDetector( + onTap: (){ + Navigator.pop(context); // 点击图片返回 + }, + child: Container( + child: RotatedBox( + quarterTurns: -1, + child: PhotoView( + imageProvider: NetworkImage(imageUrl), + ), + ), ), ), ); diff --git a/lib/main/lockDetail/videoLog/widget/video_thumbnail.dart b/lib/main/lockDetail/videoLog/widget/video_thumbnail.dart index de2259a6..06b63b59 100644 --- a/lib/main/lockDetail/videoLog/widget/video_thumbnail.dart +++ b/lib/main/lockDetail/videoLog/widget/video_thumbnail.dart @@ -49,14 +49,17 @@ class _VideoThumbnailState extends State { children: [ // 如果视频已经初始化,则显示视频玩家 _controller.value.isInitialized - ? AspectRatio( - aspectRatio: 1 / 1, // 强制正方形比例 - child: FittedBox( - fit: BoxFit.cover, // 确保视频封面铺满空间 - child: SizedBox( - width: _controller.value.size.width, - height: _controller.value.size.height, - child: VideoPlayer(_controller), + ? RotatedBox( + quarterTurns: -1, + child: AspectRatio( + aspectRatio: 1 / 1, // 强制正方形比例 + child: FittedBox( + fit: BoxFit.cover, // 确保视频封面铺满空间 + child: SizedBox( + width: _controller.value.size.width, + height: _controller.value.size.height, + child: VideoPlayer(_controller), + ), ), ), )