fix:增加视频日志中的“已下载”功能和“云存”当中的下载功能
This commit is contained in:
parent
98fadea001
commit
0e20fd57f2
@ -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<VideoLogLogic>();
|
||||
|
||||
Future<void> deleteLockCloudStorageList() async {
|
||||
final VersionUndateEntity entity =
|
||||
await ApiRepository.to.deleteLockCloudStorageList(
|
||||
@ -86,6 +92,146 @@ class EditVideoLogLogic extends BaseGetXController {
|
||||
}
|
||||
}
|
||||
|
||||
// 批量删除指定文件路径列表中的文件
|
||||
Future<bool> deleteDownloadsByPaths(List<String> 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<String, dynamic> logData =
|
||||
Map<String, dynamic>.from(json.decode(content));
|
||||
|
||||
// 过滤出需要保留的文件
|
||||
final filteredLogData = <String, dynamic>{};
|
||||
|
||||
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<void> recordDownloadTime(String filePath) async {
|
||||
final appDocDir = await getApplicationDocumentsDirectory();
|
||||
final logFilePath = '${appDocDir.path}/download_log.json';
|
||||
|
||||
// 读取现有的日志文件
|
||||
Map<String, int> logData = {};
|
||||
if (await File(logFilePath).exists()) {
|
||||
final content = await File(logFilePath).readAsString();
|
||||
logData = Map<String, int>.from(json.decode(content));
|
||||
}
|
||||
|
||||
// 添加新的下载记录
|
||||
logData[filePath] = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
// 写入日志文件
|
||||
await File(logFilePath).writeAsString(json.encode(logData));
|
||||
}
|
||||
|
||||
// 下载文件方法(支持视频和图片)
|
||||
Future<String?> 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<bool> _requestPermission() async {
|
||||
final status = await Permission.storage.request();
|
||||
|
||||
@ -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<EditVideoLogPage> {
|
||||
Widget bottomBottomBtnWidget() {
|
||||
return SizedBox(
|
||||
width: 1.sw,
|
||||
child:
|
||||
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
||||
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: <Widget>[
|
||||
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<void> _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: <String, List<RecordListData>>{
|
||||
@ -240,11 +257,28 @@ class _EditVideoLogPageState extends State<EditVideoLogPage> {
|
||||
}
|
||||
|
||||
Future<void> _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<String> 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<EditVideoLogPage> {
|
||||
_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');
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,9 +12,13 @@ class EditVideoLogState {
|
||||
if (map['lockId'] != null) {
|
||||
getLockId.value = map['lockId'];
|
||||
}
|
||||
if (map['isNavLocal'] != null) {
|
||||
isNavLocal.value = map['isNavLocal'];
|
||||
}
|
||||
}
|
||||
RxList<RecordListData> selectVideoLogList = <RecordListData>[].obs; //选中的视频列表
|
||||
RxBool isSelectAll = false.obs;
|
||||
RxList videoLogList = [].obs; //全部的视频列表
|
||||
RxList videoLogList = <CloudStorageData>[].obs; //全部的视频列表
|
||||
RxInt getLockId = 0.obs;
|
||||
var isNavLocal = false.obs;
|
||||
}
|
||||
|
||||
@ -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<void> listDownloadedVideos() async {
|
||||
Directory appDocDir = await getApplicationDocumentsDirectory();
|
||||
Directory downloadsDir = Directory('${appDocDir.path}/downloads');
|
||||
|
||||
if (await downloadsDir.exists()) {
|
||||
List<FileSystemEntity> 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<List<CloudStorageData>> groupDownloadsByDay() async {
|
||||
final appDocDir = await getApplicationDocumentsDirectory();
|
||||
final logFilePath = '${appDocDir.path}/download_log.json';
|
||||
|
||||
// 初始化结果映射
|
||||
Map<String, List<RecordListData>> groupedDownloads = {};
|
||||
|
||||
if (await File(logFilePath).exists()) {
|
||||
final content = await File(logFilePath).readAsString();
|
||||
final logData = Map<String, int>.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> cloudStorageData = [];
|
||||
groupedDownloads.forEach((dateKey, recordList) {
|
||||
cloudStorageData.add(
|
||||
CloudStorageData(date: dateKey, recordList: recordList),
|
||||
);
|
||||
});
|
||||
state.lockVideoList.value = cloudStorageData;
|
||||
return cloudStorageData;
|
||||
}
|
||||
|
||||
// 获取文件的下载时间
|
||||
Future<int?> 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<String, int>.from(json.decode(content));
|
||||
return logData[filePath];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 清空下载目录和日志文件
|
||||
Future<bool> 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();
|
||||
|
||||
@ -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<VideoLogPage> {
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
logic.groupDownloadsByDay();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -50,53 +53,94 @@ class _VideoLogPageState extends State<VideoLogPage> {
|
||||
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: <Widget>[
|
||||
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: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
left: 20.w, top: 15.w, bottom: 15.w),
|
||||
child: Row(children: <Widget>[
|
||||
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: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
left: 20.w, top: 15.w, bottom: 15.w),
|
||||
child: Row(children: <Widget>[
|
||||
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: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
left: 20.w, top: 15.w, bottom: 15.w),
|
||||
child: Row(children: <Widget>[
|
||||
Text('2023.10.2$index',
|
||||
style: TextStyle(fontSize: 20.sp)),
|
||||
])),
|
||||
mainListView(index, CloudStorageData()),
|
||||
],
|
||||
);
|
||||
})
|
||||
: NoData())),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
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<VideoLogPage> {
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
state.isNavLocal.value = false;
|
||||
state.lockVideoList.clear();
|
||||
// logic.clearDownloads();
|
||||
});
|
||||
},
|
||||
child: Obx(() => Text('云存'.tr,
|
||||
@ -122,21 +168,27 @@ class _VideoLogPageState extends State<VideoLogPage> {
|
||||
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<VideoLogPage> {
|
||||
iconSize: 30,
|
||||
color: Colors.black54,
|
||||
onPressed: () {
|
||||
Get.toNamed(Routers.editVideoLogPage, arguments: <String, Object>{
|
||||
'videoDataList': state.videoLogList.value,
|
||||
'lockId': state.getLockId.value
|
||||
});
|
||||
if (state.isNavLocal.value) {
|
||||
Get.toNamed(Routers.editVideoLogPage,
|
||||
arguments: <String, Object>{
|
||||
'videoDataList': state.lockVideoList.value,
|
||||
'lockId': state.getLockId.value,
|
||||
'isNavLocal': state.isNavLocal.value
|
||||
});
|
||||
} else {
|
||||
Get.toNamed(Routers.editVideoLogPage,
|
||||
arguments: <String, Object>{
|
||||
'videoDataList': state.videoLogList.value,
|
||||
'lockId': state.getLockId.value,
|
||||
'isNavLocal': state.isNavLocal.value
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
// TextButton(
|
||||
@ -286,6 +349,28 @@ class _VideoLogPageState extends State<VideoLogPage> {
|
||||
);
|
||||
}
|
||||
|
||||
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<VideoLogPage> {
|
||||
),
|
||||
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<VideoLogPage> {
|
||||
_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');
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ class VideoLogState {
|
||||
var localList = [];
|
||||
var getLockId = 0.obs;
|
||||
var videoLogList = <CloudStorageData>[].obs;
|
||||
var lockVideoList = <CloudStorageData>[].obs;
|
||||
var videoCoverList = <Uint8List>[].obs;
|
||||
|
||||
VideoLogState() {
|
||||
|
||||
@ -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 动态选择图片加载方式
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据 imageUrl 判断并返回对应的 ImageProvider
|
||||
ImageProvider<Object> _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');
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user