analysis_controller.dart 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. import 'dart:math';
  2. import 'package:classify_photo/classify_photo.dart';
  3. import 'package:clean/base/base_controller.dart';
  4. import 'package:clean/module/analysis/analysis_state.dart';
  5. import 'package:clean/utils/expand.dart';
  6. import 'package:flutter/cupertino.dart';
  7. import 'package:flutter_screenutil/flutter_screenutil.dart';
  8. import 'package:get/get.dart';
  9. import 'package:get/get_core/src/get_main.dart';
  10. import 'package:get/get_rx/src/rx_types/rx_types.dart';
  11. import 'package:permission_handler/permission_handler.dart';
  12. import 'package:wechat_camera_picker/wechat_camera_picker.dart';
  13. import '../../model/asset_info.dart';
  14. import '../../utils/file_utils.dart';
  15. import '../../utils/image_util.dart';
  16. import '../../utils/toast_util.dart';
  17. import '../image_picker/image_picker_assets.dart';
  18. class AnalysisController extends BaseController {
  19. // 是否为编辑状态
  20. RxBool isEdit = false.obs;
  21. // 使用共享状态
  22. RxList<AssetInfo> imageList = AnalysisState.imageList;
  23. RxMap<String, List<AssetInfo>> assetsByMonth = AnalysisState.assetsByMonth;
  24. // 获取月份数量
  25. int get monthCount => assetsByMonth.length;
  26. // 获取总图片数量
  27. int get totalAssetCount =>
  28. assetsByMonth.values.fold(0, (sum, list) => sum + list.length);
  29. // 存储选中的图片ID
  30. final RxSet<String> selectedAssets = <String>{}.obs;
  31. // 是否全选
  32. RxBool isAllSelected = false.obs;
  33. // 选中图片的总容量(字节)
  34. final RxInt selectedTotalSize = 0.obs;
  35. @override
  36. void onInit() {
  37. // TODO: implement onInit
  38. super.onInit();
  39. loadAssets();
  40. }
  41. // 加载并分组图片
  42. Future<void> loadAssets() async {
  43. var newImageList = <AssetInfo>[];
  44. newImageList = await FileUtils.getAllAssets(FileType.analysis);
  45. AnalysisState.imageList.value = newImageList;
  46. AnalysisState.updateMonthlyAssets();
  47. if (newImageList.isEmpty) {
  48. isEdit.value = false;
  49. return;
  50. }
  51. // 清空现有数据
  52. assetsByMonth.clear();
  53. // 按月份分组
  54. for (var asset in newImageList) {
  55. final monthKey = ImageUtil.getMonthKey(asset.createDateTime);
  56. if (!assetsByMonth.containsKey(monthKey)) {
  57. assetsByMonth[monthKey] = [];
  58. }
  59. assetsByMonth[monthKey]!.add(asset);
  60. asset.dateTitle = ImageUtil.formatMonthKey(monthKey);
  61. }
  62. newImageList.sort((a, b) => b.createDateTime.compareTo(a.createDateTime));
  63. AnalysisState.imageList.value = newImageList;
  64. AnalysisState.updateMonthlyAssets();
  65. // 对每个月份内的图片按时间排序(新的在前)
  66. assetsByMonth.forEach((key, list) {
  67. list.sort((a, b) => b.createDateTime.compareTo(a.createDateTime));
  68. });
  69. // 打印分组结果
  70. assetsByMonth.forEach((key, assets) {
  71. print('${ImageUtil.formatMonthKey(key)}: ${assets.length} photos');
  72. });
  73. }
  74. // 上传按钮点击
  75. void uploadBtnClick() {
  76. showCupertinoModalPopup(
  77. context: Get.context!,
  78. builder: (context) {
  79. return CupertinoActionSheet(
  80. actions: <Widget>[
  81. //操作按钮集合
  82. CupertinoActionSheetAction(
  83. onPressed: () {
  84. Navigator.pop(context);
  85. openGallery();
  86. },
  87. child: Text(
  88. 'Upload from Gallery',
  89. style: TextStyle(
  90. color: "#007AFF".color,
  91. fontWeight: FontWeight.w500,
  92. fontSize: 16.sp,
  93. ),
  94. ),
  95. ),
  96. CupertinoActionSheetAction(
  97. onPressed: () {
  98. Navigator.pop(context);
  99. openCamera();
  100. },
  101. child: Text(
  102. 'Take and Upload',
  103. style: TextStyle(
  104. color: "#007AFF".color,
  105. fontWeight: FontWeight.w500,
  106. fontSize: 16.sp,
  107. ),
  108. ),
  109. ),
  110. ],
  111. cancelButton: CupertinoActionSheetAction(
  112. //取消按钮
  113. onPressed: () {
  114. Navigator.pop(context);
  115. },
  116. child: Text(
  117. 'Cancel',
  118. style: TextStyle(
  119. color: "#007AFF".color,
  120. fontWeight: FontWeight.w500,
  121. fontSize: 16.sp,
  122. ),
  123. ),
  124. ),
  125. );
  126. },
  127. );
  128. }
  129. // 保存并刷新图片列表
  130. Future<void> saveAndRefreshAssets(List<AssetEntity> assets) async {
  131. for (var asset in assets) {
  132. await FileUtils.saveAsset(FileType.analysis, asset);
  133. }
  134. // 重新加载图片列表
  135. loadAssets();
  136. }
  137. // 选择/取消选择图片
  138. void toggleSelectAsset(String assetId) {
  139. final asset = imageList.firstWhere((asset) => asset.id == assetId);
  140. if (selectedAssets.contains(assetId)) {
  141. selectedAssets.remove(assetId);
  142. if (asset.size != null) {
  143. selectedTotalSize.value -= asset.size!;
  144. }
  145. } else {
  146. selectedAssets.add(assetId);
  147. if (asset.size != null) {
  148. selectedTotalSize.value += asset.size!;
  149. }
  150. }
  151. // 更新全选状态
  152. isAllSelected.value = selectedAssets.length == imageList.length;
  153. }
  154. // 全选/取消全选
  155. void toggleSelectAll() {
  156. if (isAllSelected.value) {
  157. selectedAssets.clear();
  158. selectedTotalSize.value = 0;
  159. } else {
  160. selectedAssets.addAll(imageList.map((asset) => asset.id));
  161. selectedTotalSize.value = imageList.fold(
  162. 0, (sum, asset) => sum + (asset.size != null ? asset.size! : 0));
  163. }
  164. isAllSelected.value = !isAllSelected.value;
  165. }
  166. // 退出编辑模式时清空选择
  167. void exitEditMode() {
  168. isEdit.value = false;
  169. selectedAssets.clear();
  170. isAllSelected.value = false;
  171. selectedTotalSize.value = 0;
  172. }
  173. // 删除文件
  174. void deleteBtnClick() {
  175. // 获取要删除的资产
  176. final assetsToDelete =
  177. imageList.where((asset) => selectedAssets.contains(asset.id)).toList();
  178. for (var asset in assetsToDelete) {
  179. FileUtils.deleteAsset(FileType.analysis, asset.id.substring(0, 36));
  180. }
  181. selectedTotalSize.value = 0;
  182. loadAssets();
  183. }
  184. // 格式化文件大小显示
  185. String formatFileSize(int bytes) {
  186. if (bytes <= 0) return "Delete";
  187. final units = ['B', 'KB', 'MB', 'GB'];
  188. int digitGroups = (log(bytes) / log(1024)).floor();
  189. if (bytes == 0) {
  190. return "Delete";
  191. } else {
  192. return "Delete(${(bytes / pow(1024, digitGroups)).toStringAsFixed(1)} ${units[digitGroups]})";
  193. }
  194. }
  195. // 开启图库
  196. Future<void> openGallery() async {
  197. var status = await Permission.photos.status;
  198. if (status == PermissionStatus.granted) {
  199. List<AssetEntity> assets = <AssetEntity>[];
  200. for (var asset in imageList) {
  201. var newAsset = await asset.toAssetEntity();
  202. if (newAsset != null) {
  203. assets.add(newAsset);
  204. }
  205. }
  206. List<AssetEntity>? pickList = await ImagePickAssets.pick();
  207. if (pickList != null && pickList.isNotEmpty) {
  208. await saveAndRefreshAssets(pickList);
  209. final asset = pickList.first;
  210. final file = await asset.originFile;
  211. // final exifInfo = await ClassifyPhoto().getPhotoExif(file!.path);
  212. // print(exifInfo);
  213. }
  214. } else {
  215. ToastUtil.show("请先开启权限");
  216. }
  217. }
  218. // 开启相机
  219. Future<void> openCamera() async {
  220. final entity = await CameraPicker.pickFromCamera(
  221. Get.context!,
  222. pickerConfig: const CameraPickerConfig(),
  223. );
  224. if (entity != null) {
  225. await saveAndRefreshAssets([entity]);
  226. }
  227. }
  228. }