diff --git a/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_logic.dart b/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_logic.dart index 8ad36896..116b8819 100755 --- a/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_logic.dart +++ b/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_logic.dart @@ -1,11 +1,15 @@ +import 'dart:convert'; import 'dart:io'; +import 'package:crypto/crypto.dart'; 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/app_settings/app_settings.dart'; import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart'; +import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_logic.dart'; import 'package:star_lock/network/api_repository.dart'; import 'package:star_lock/tools/baseGetXController.dart'; import 'package:star_lock/versionUndate/versionUndate_entity.dart'; @@ -14,6 +18,8 @@ import 'editVideoLog_state.dart'; class EditVideoLogLogic extends BaseGetXController { EditVideoLogState state = EditVideoLogState(); + final VideoLogLogic videoLogLogic = Get.find(); + Future deleteLockCloudStorageList() async { final VersionUndateEntity entity = await ApiRepository.to.deleteLockCloudStorageList( @@ -86,6 +92,146 @@ class EditVideoLogLogic extends BaseGetXController { } } + // 批量删除指定文件路径列表中的文件 + Future deleteDownloadsByPaths(List filePaths) async { + try { + showEasyLoading(); + // 获取应用专属目录 + Directory appDocDir = await getApplicationDocumentsDirectory(); + final logFilePath = '${appDocDir.path}/download_log.json'; + + if (await File(logFilePath).exists()) { + final content = await File(logFilePath).readAsString(); + Map logData = + Map.from(json.decode(content)); + + // 过滤出需要保留的文件 + final filteredLogData = {}; + + for (final entry in logData.entries) { + final filePath = entry.key; + + if (!filePaths.contains(filePath)) { + filteredLogData[filePath] = entry.value; // 保留其他文件 + } else { + // 删除文件 + final file = File(filePath); + if (await file.exists()) { + await file.delete(); + print('已删除文件:$filePath'); + } + } + } + + // 更新日志文件 + await File(logFilePath).writeAsString(json.encode(filteredLogData)); + showToast('删除成功'.tr); + + // 重新分组统计下载文件 + await videoLogLogic.groupDownloadsByDay(); + + Get.back(); + return true; + } else { + print('日志文件不存在'); + return false; + } + } catch (e) { + print('批量删除失败:$e'); + return false; + } finally { + dismissEasyLoading(); + } + } + + // 根据URL生成唯一的文件名(MD5哈希值) + String getFileNameFromUrl(String url, String extension) { + final hash = md5.convert(utf8.encode(url)).toString(); // 使用 md5 生成哈希值 + return '$hash.$extension'; + } + + Future recordDownloadTime(String filePath) async { + final appDocDir = await getApplicationDocumentsDirectory(); + final logFilePath = '${appDocDir.path}/download_log.json'; + + // 读取现有的日志文件 + Map logData = {}; + if (await File(logFilePath).exists()) { + final content = await File(logFilePath).readAsString(); + logData = Map.from(json.decode(content)); + } + + // 添加新的下载记录 + logData[filePath] = DateTime.now().millisecondsSinceEpoch; + + // 写入日志文件 + await File(logFilePath).writeAsString(json.encode(logData)); + } + +// 下载文件方法(支持视频和图片) + Future downloadFile(String? url) async { + if (url == null || url.isEmpty) { + print('URL不能为空'); + return null; + } + + // 请求存储权限 + if (await Permission.storage.request().isGranted) { + try { + // 获取应用专属目录(避免与其他应用冲突) + Directory appDocDir = await getApplicationDocumentsDirectory(); + + // 根据URL生成唯一文件名(自动识别扩展名) + String extension = _getFileTypeFromUrl(url); // 自动检测文件类型 + String fileName = getFileNameFromUrl(url, extension); // 根据URL生成唯一文件名 + String savePath = '${appDocDir.path}/downloads/$fileName'; // 自定义保存路径 + + // 确保目录存在 + final dir = Directory('${appDocDir.path}/downloads'); + if (!await dir.exists()) { + await dir.create(recursive: true); + } + + // 检查文件是否已存在 + File file = File(savePath); + if (await file.exists()) { + print('文件已存在,无需重新下载:$savePath'); + return savePath; // 文件已存在,直接返回路径 + } + + // 下载文件 + await Dio().download(url, savePath, + onReceiveProgress: (received, total) { + if (total != -1) { + print('下载进度: ${(received / total * 100).toStringAsFixed(0)}%'); + } + }); + + // 记录下载时间 + await recordDownloadTime(savePath); + + print('文件已成功下载到:$savePath'); + + // 返回下载路径以便后续使用 + return savePath; + } catch (e) { + print('下载失败:$e'); + return null; + } + } else { + print('未获取存储权限'); + return null; + } + } + +// 根据URL自动检测文件类型 + String _getFileTypeFromUrl(String url) { + final uri = Uri.parse(url); + final path = uri.path; + final extension = path.split('.').last.toLowerCase(); + return extension.isNotEmpty ? extension : 'unknown'; + } + // 请求存储权限 Future _requestPermission() async { final status = await Permission.storage.request(); diff --git a/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_page.dart b/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_page.dart index 86faa5b8..c2475b4e 100755 --- a/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_page.dart +++ b/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -180,55 +182,70 @@ class _EditVideoLogPageState extends State { Widget bottomBottomBtnWidget() { return SizedBox( width: 1.sw, - child: - Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - bottomBtnItemWidget( - 'images/main/icon_lockDetail_monitoringDownloadVideo.png', - '下载'.tr, - Colors.white, - _onDownLoadClick, - ), - SizedBox(width: 100.w), - bottomBtnItemWidget( - 'images/main/icon_lockDetail_monitoringDeletVideo.png', - '删除'.tr, - AppColors.mainColor, - _onDelClick, - ) - ]), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + state.isNavLocal.value == false + ? bottomBtnItemWidget( + 'images/main/icon_lockDetail_monitoringDownloadVideo.png', + '下载'.tr, + Colors.white, + _onDownLoadClick, + ) + : SizedBox.shrink(), + state.isNavLocal.value == false + ? SizedBox(width: 100.w) + : SizedBox.shrink(), + bottomBtnItemWidget( + 'images/main/icon_lockDetail_monitoringDeletVideo.png', + '删除'.tr, + AppColors.mainColor, + _onDelClick, + ) + ], + ), ); } Future _onDownLoadClick() async { if (state.selectVideoLogList.value.isNotEmpty) { - double _progress = 0.0; + state.selectVideoLogList.value.forEach((element) { + if (element.videoUrl != null && element.videoUrl != '') { + logic.downloadFile(element.videoUrl ?? ''); + } else if (element.imagesUrl != null && element.imagesUrl != '') { + logic.downloadFile(element.imagesUrl ?? ''); + } + }); + // 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); + // 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); + EasyLoading.showSuccess('下载完成'.tr); state.selectVideoLogList.clear(); + Get.back(); setState(() {}); // Get.toNamed(Routers.videoLogDownLoadPage, // arguments: >{ @@ -240,11 +257,28 @@ class _EditVideoLogPageState extends State { } Future _onDelClick() async { - if (state.selectVideoLogList.value.isNotEmpty) { - await logic.deleteLockCloudStorageList(); - setState(() {}); + if (state.isNavLocal.isFalse) { + if (state.selectVideoLogList.value.isNotEmpty) { + await logic.deleteLockCloudStorageList(); + setState(() {}); + } else { + logic.showToast('请选择要删除的视频'.tr); + } } else { - logic.showToast('请选择要删除的视频'.tr); + if (state.selectVideoLogList.value.isNotEmpty) { + List deletePaths = []; + state.selectVideoLogList.value.forEach((element) { + if (element.imagesUrl != null && element.imagesUrl != '') { + deletePaths.add(element.imagesUrl!); + } + if (element.videoUrl != null && element.videoUrl != '') { + deletePaths.add(element.videoUrl!); + } + }); + logic.deleteDownloadsByPaths(deletePaths); + } else { + logic.showToast('请选择要删除的视频'.tr); + } } } @@ -365,21 +399,56 @@ class _EditVideoLogPageState extends State { _buildImageItem(RecordListData recordData) { return RotatedBox( quarterTurns: -1, - child: Image.network( - recordData.imagesUrl!, + child: _buildImageWidget(recordData.imagesUrl), + ); + } + + // 根据图片路径构建对应的 Image Widget + Widget _buildImageWidget(String? imageUrl) { + if (imageUrl == null || imageUrl.isEmpty) { + // 如果图片路径为空,返回错误图片 + return Image.asset( + 'images/icon_unHaveData.png', // 错误图片路径 + fit: BoxFit.cover, + ); + } + + // 判断是否为网络地址 + if (_isNetworkUrl(imageUrl)) { + return Image.network( + imageUrl, fit: BoxFit.cover, errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) { // 图片加载失败时显示错误图片 - return RotatedBox( - quarterTurns: -1, - child: Image.asset( - 'images/icon_unHaveData.png', // 错误图片路径 - fit: BoxFit.cover, - ), + return Image.asset( + 'images/icon_unHaveData.png', // 错误图片路径 + fit: BoxFit.cover, ); }, - ), - ); + ); + } else { + // 如果是本地文件路径,则使用 FileImage 加载图片 + return Image.file( + File(imageUrl), + fit: BoxFit.cover, + errorBuilder: + (BuildContext context, Object error, StackTrace? stackTrace) { + // 文件加载失败时显示错误图片 + return Image.asset( + 'images/icon_unHaveData.png', // 错误图片路径 + fit: BoxFit.cover, + ); + }, + ); + } + } + + // 判断是否为网络地址 + bool _isNetworkUrl(String url) { + final uri = Uri.tryParse(url); + return uri != null && + uri.scheme.isNotEmpty && + uri.scheme.startsWith('http'); } } diff --git a/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_state.dart b/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_state.dart index e7ae301e..359e6069 100755 --- a/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_state.dart +++ b/lib/main/lockDetail/videoLog/editVideoLog/editVideoLog_state.dart @@ -12,9 +12,13 @@ class EditVideoLogState { if (map['lockId'] != null) { getLockId.value = map['lockId']; } + if (map['isNavLocal'] != null) { + isNavLocal.value = map['isNavLocal']; + } } RxList selectVideoLogList = [].obs; //选中的视频列表 RxBool isSelectAll = false.obs; - RxList videoLogList = [].obs; //全部的视频列表 + RxList videoLogList = [].obs; //全部的视频列表 RxInt getLockId = 0.obs; + var isNavLocal = false.obs; } diff --git a/lib/main/lockDetail/videoLog/videoLog/videoLog_logic.dart b/lib/main/lockDetail/videoLog/videoLog/videoLog_logic.dart index b19264a5..e90c9793 100755 --- a/lib/main/lockDetail/videoLog/videoLog/videoLog_logic.dart +++ b/lib/main/lockDetail/videoLog/videoLog/videoLog_logic.dart @@ -1,5 +1,8 @@ +import 'dart:convert'; +import 'dart:io'; import 'dart:typed_data'; +import 'package:path_provider/path_provider.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'; @@ -28,6 +31,117 @@ class VideoLogLogic extends BaseGetXController { } } + // 列出已下载的视频文件 + Future listDownloadedVideos() async { + Directory appDocDir = await getApplicationDocumentsDirectory(); + Directory downloadsDir = Directory('${appDocDir.path}/downloads'); + + if (await downloadsDir.exists()) { + List files = downloadsDir.listSync(); + final list = + files.where((file) => file is File).map((file) => file.path).toList(); + list.forEach((element) async { + final downloadTime = await getDownloadTime(element); + // 获取每一个文件夹对应的下载时间 + }); + } + } + + // 按天分组统计下载文件 + Future> groupDownloadsByDay() async { + final appDocDir = await getApplicationDocumentsDirectory(); + final logFilePath = '${appDocDir.path}/download_log.json'; + + // 初始化结果映射 + Map> groupedDownloads = {}; + + if (await File(logFilePath).exists()) { + final content = await File(logFilePath).readAsString(); + final logData = Map.from(json.decode(content)); + + // 遍历所有记录 + logData.forEach((filePath, timestamp) { + final downloadDateTime = DateTime.fromMillisecondsSinceEpoch(timestamp); + final dateKey = + '${downloadDateTime.year}-${downloadDateTime.month.toString().padLeft(2, '0')}-${downloadDateTime.day.toString().padLeft(2, '0')}'; + + // 如果日期不存在于映射中,则初始化为空列表 + if (!groupedDownloads.containsKey(dateKey)) { + groupedDownloads[dateKey] = []; + } + + // 将文件记录添加到对应日期的列表中 + if (filePath.endsWith('.jpg')) { + groupedDownloads[dateKey]?.add( + RecordListData(operateDate: timestamp, imagesUrl: filePath), + ); + } else if (filePath.endsWith('.mp4')) { + groupedDownloads[dateKey]?.add( + RecordListData(operateDate: timestamp, videoUrl: filePath), + ); + } + }); + } + + // 将分组结果转换为 CloudStorageData 列表 + List cloudStorageData = []; + groupedDownloads.forEach((dateKey, recordList) { + cloudStorageData.add( + CloudStorageData(date: dateKey, recordList: recordList), + ); + }); + state.lockVideoList.value = cloudStorageData; + return cloudStorageData; + } + + // 获取文件的下载时间 + Future getDownloadTime(String filePath) async { + final appDocDir = await getApplicationDocumentsDirectory(); + final logFilePath = '${appDocDir.path}/download_log.json'; + + if (await File(logFilePath).exists()) { + final content = await File(logFilePath).readAsString(); + final logData = Map.from(json.decode(content)); + return logData[filePath]; + } + return null; + } + + // 清空下载目录和日志文件 + Future clearDownloads() async { + try { + // 获取应用专属目录 + Directory appDocDir = await getApplicationDocumentsDirectory(); + + // 删除下载目录中的所有文件 + final downloadsDir = Directory('${appDocDir.path}/downloads'); + if (await downloadsDir.exists()) { + await downloadsDir.list().forEach((entity) { + if (entity is File) { + entity.delete(recursive: true); + } + }); + print('下载目录已清空'); + } else { + print('下载目录不存在'); + } + + // 删除日志文件 + final logFilePath = '${appDocDir.path}/download_log.json'; + if (await File(logFilePath).exists()) { + await File(logFilePath).delete(); + print('日志文件已删除'); + } else { + print('日志文件不存在'); + } + + return true; + } catch (e) { + print('清空失败:$e'); + return false; + } + } + @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 7911a215..97bd6ab6 100755 --- a/lib/main/lockDetail/videoLog/videoLog/videoLog_page.dart +++ b/lib/main/lockDetail/videoLog/videoLog/videoLog_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; @@ -30,6 +32,7 @@ class _VideoLogPageState extends State { void initState() { // TODO: implement initState super.initState(); + logic.groupDownloadsByDay(); } @override @@ -50,53 +53,94 @@ class _VideoLogPageState extends State { Visibility(visible: state.isNavLocal.value, child: localTip()), // title加编辑按钮 editVideoTip(), - Obx(() => Visibility( + Obx( + () => Visibility( visible: !state.isNavLocal.value, - child: Expanded( - child: ListView.builder( - itemCount: state.videoLogList.length, - itemBuilder: (BuildContext c, int index) { - final CloudStorageData item = state.videoLogList[index]; - return Column( - children: [ - Container( + child: state.videoLogList.length > 0 + ? Expanded( + child: ListView.builder( + itemCount: state.videoLogList.length, + itemBuilder: (BuildContext c, int index) { + final CloudStorageData item = + state.videoLogList[index]; + return Column( + children: [ + Container( + margin: EdgeInsets.only( + left: 20.w, top: 15.w, bottom: 15.w), + child: Row(children: [ + Text(item.date ?? '', + style: TextStyle(fontSize: 20.sp)), + ])), + mainListView(index, item) + ], + ); + }, + ), + ) + : _buildNotData(), + ), + ), + // 本地顶部 + Obx( + () => Visibility( + visible: state.isNavLocal.value, + child: state.lockVideoList.length > 0 + ? Expanded( + child: ListView.builder( + itemCount: state.lockVideoList.length, + itemBuilder: (BuildContext c, int index) { + final CloudStorageData item = + state.lockVideoList[index]; + return Column( + children: [ + Container( margin: EdgeInsets.only( left: 20.w, top: 15.w, bottom: 15.w), - child: Row(children: [ - Text(item.date ?? '', - style: TextStyle(fontSize: 20.sp)), - ])), - mainListView(index, item) - ], - ); - })))), - // 本地顶部 - Visibility( - visible: state.isNavLocal.value, - child: Expanded( - child: state.localList.isNotEmpty - ? ListView.builder( - itemCount: 5, - itemBuilder: (BuildContext c, int index) { - return Column( - children: [ - Container( - margin: EdgeInsets.only( - left: 20.w, top: 15.w, bottom: 15.w), - child: Row(children: [ - Text('2023.10.2$index', - style: TextStyle(fontSize: 20.sp)), - ])), - mainListView(index, CloudStorageData()), - ], - ); - }) - : NoData())), + child: Row( + children: [ + Text(item.date ?? '', + style: TextStyle(fontSize: 20.sp)), + ], + ), + ), + lockMainListView(index, item) + ], + ); + }, + ), + ) + : _buildNotData(), + ), + ), ], ), ); } + Widget _buildNotData() { + return Expanded( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + 'images/icon_noData.png', + width: 160.w, + height: 180.h, + ), + Text( + '暂无数据'.tr, + style: TextStyle( + color: AppColors.darkGrayTextColor, fontSize: 22.sp), + ) + ], + ), + ), + ); + } + // nav按钮 Widget navBtn() { return SizedBox( @@ -109,6 +153,8 @@ class _VideoLogPageState extends State { onPressed: () { setState(() { state.isNavLocal.value = false; + state.lockVideoList.clear(); + // logic.clearDownloads(); }); }, child: Obx(() => Text('云存'.tr, @@ -122,21 +168,27 @@ class _VideoLogPageState extends State { fontSize: 28.sp, fontWeight: FontWeight.w600)))), TextButton( - onPressed: () { - setState(() { - state.isNavLocal.value = true; - }); - }, - child: Obx(() => Text('本地'.tr, - style: state.isNavLocal.value == true - ? TextStyle( - color: Colors.white, - fontSize: 28.sp, - fontWeight: FontWeight.w600) - : TextStyle( - color: Colors.grey, - fontSize: 26.sp, - fontWeight: FontWeight.w600)))), + onPressed: () { + setState(() { + state.isNavLocal.value = true; + logic.groupDownloadsByDay(); + }); + }, + child: Obx( + () => Text( + '已下载'.tr, + style: state.isNavLocal.value == true + ? TextStyle( + color: Colors.white, + fontSize: 28.sp, + fontWeight: FontWeight.w600) + : TextStyle( + color: Colors.grey, + fontSize: 26.sp, + fontWeight: FontWeight.w600), + ), + ), + ), ], ), ); @@ -243,10 +295,21 @@ class _VideoLogPageState extends State { iconSize: 30, color: Colors.black54, onPressed: () { - Get.toNamed(Routers.editVideoLogPage, arguments: { - 'videoDataList': state.videoLogList.value, - 'lockId': state.getLockId.value - }); + if (state.isNavLocal.value) { + Get.toNamed(Routers.editVideoLogPage, + arguments: { + 'videoDataList': state.lockVideoList.value, + 'lockId': state.getLockId.value, + 'isNavLocal': state.isNavLocal.value + }); + } else { + Get.toNamed(Routers.editVideoLogPage, + arguments: { + 'videoDataList': state.videoLogList.value, + 'lockId': state.getLockId.value, + 'isNavLocal': state.isNavLocal.value + }); + } }, ) // TextButton( @@ -286,6 +349,28 @@ class _VideoLogPageState extends State { ); } + Widget lockMainListView(int index, CloudStorageData itemData) { + return GridView.builder( + padding: EdgeInsets.only(left: 15.w, right: 15.w), + itemCount: itemData.recordList!.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + //横轴元素个数 + crossAxisCount: 3, + //纵轴间距 + mainAxisSpacing: 15.w, + // 横轴间距 + crossAxisSpacing: 15.w, + //子组件宽高长度比例 + childAspectRatio: itemW / itemH), + itemBuilder: (BuildContext context, int index) { + final RecordListData recordData = itemData.recordList![index]; + return videoItem(recordData); + }, + ); + } + Widget videoItem(RecordListData recordData) { return GestureDetector( onTap: () { @@ -323,9 +408,10 @@ class _VideoLogPageState extends State { ), SizedBox(height: 5.h), Text( - DateTool().dateToYMDHNString(recordData.operateDate.toString()), - textAlign: TextAlign.center, - style: TextStyle(fontSize: 18.sp)) + DateTool().dateToYMDHNString(recordData.operateDate.toString()), + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18.sp), + ) ], ), ), @@ -347,21 +433,56 @@ class _VideoLogPageState extends State { _buildImageItem(RecordListData recordData) { return RotatedBox( quarterTurns: -1, - child: Image.network( - recordData.imagesUrl!, + child: _buildImageWidget(recordData.imagesUrl), + ); + } + + // 根据图片路径构建对应的 Image Widget + Widget _buildImageWidget(String? imageUrl) { + if (imageUrl == null || imageUrl.isEmpty) { + // 如果图片路径为空,返回错误图片 + return Image.asset( + 'images/icon_unHaveData.png', // 错误图片路径 + fit: BoxFit.cover, + ); + } + + // 判断是否为网络地址 + if (_isNetworkUrl(imageUrl)) { + return Image.network( + imageUrl, fit: BoxFit.cover, errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) { // 图片加载失败时显示错误图片 - return RotatedBox( - quarterTurns: -1, - child: Image.asset( - 'images/icon_unHaveData.png', // 错误图片路径 - fit: BoxFit.cover, - ), + return Image.asset( + 'images/icon_unHaveData.png', // 错误图片路径 + fit: BoxFit.cover, ); }, - ), - ); + ); + } else { + // 如果是本地文件路径,则使用 FileImage 加载图片 + return Image.file( + File(imageUrl), + fit: BoxFit.cover, + errorBuilder: + (BuildContext context, Object error, StackTrace? stackTrace) { + // 文件加载失败时显示错误图片 + return Image.asset( + 'images/icon_unHaveData.png', // 错误图片路径 + fit: BoxFit.cover, + ); + }, + ); + } + } + + // 判断是否为网络地址 + bool _isNetworkUrl(String url) { + final uri = Uri.tryParse(url); + return uri != null && + uri.scheme.isNotEmpty && + uri.scheme.startsWith('http'); } } diff --git a/lib/main/lockDetail/videoLog/videoLog/videoLog_state.dart b/lib/main/lockDetail/videoLog/videoLog/videoLog_state.dart index b703686b..8b2a7960 100755 --- a/lib/main/lockDetail/videoLog/videoLog/videoLog_state.dart +++ b/lib/main/lockDetail/videoLog/videoLog/videoLog_state.dart @@ -8,6 +8,7 @@ class VideoLogState { var localList = []; var getLockId = 0.obs; var videoLogList = [].obs; + var lockVideoList = [].obs; var videoCoverList = [].obs; VideoLogState() { diff --git a/lib/main/lockDetail/videoLog/widget/full_screenImage_page.dart b/lib/main/lockDetail/videoLog/widget/full_screenImage_page.dart index f2da5bf9..1166b3de 100644 --- a/lib/main/lockDetail/videoLog/widget/full_screenImage_page.dart +++ b/lib/main/lockDetail/videoLog/widget/full_screenImage_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view_gallery.dart'; @@ -11,18 +13,42 @@ class FullScreenImagePage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( body: GestureDetector( - onTap: (){ + onTap: () { Navigator.pop(context); // 点击图片返回 }, child: Container( child: RotatedBox( quarterTurns: -1, child: PhotoView( - imageProvider: NetworkImage(imageUrl), + imageProvider: + _getImageProvider(imageUrl), // 根据 imageUrl 动态选择图片加载方式 ), ), ), ), ); } -} \ No newline at end of file + +// 根据 imageUrl 判断并返回对应的 ImageProvider + ImageProvider _getImageProvider(String? imageUrl) { + if (imageUrl == null || imageUrl.isEmpty) { + // 如果图片路径为空,返回默认的占位图片 + return AssetImage('images/icon_unHaveData.png'); // 默认占位图片路径 + } + + // 判断是否为网络地址 + if (_isNetworkUrl(imageUrl)) { + return NetworkImage(imageUrl); // 网络图片 + } else { + return FileImage(File(imageUrl)); // 本地文件图片 + } + } + +// 判断是否为网络地址 + bool _isNetworkUrl(String url) { + final uri = Uri.tryParse(url); + return uri != null && + uri.scheme.isNotEmpty && + uri.scheme.startsWith('http'); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 7d0cc89b..feb2c0ee 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -186,7 +186,7 @@ dependencies: lpinyin: ^2.0.3 #加密解密 # encrypt: ^5.0.1 -# crypto: ^3.0.3 + crypto: ^3.0.3 pointycastle: ^3.7.3 # 使用最新版本 date_format: ^2.0.7