Преглед изворни кода

1.完善删除动画,增加礼花效果
2.修复预览页面点击删除按钮周边区域也会触发删除操作
3.增加其他删除功能的删除动画
4.修改android包名
5.修复相似图片删除单独一组没更新的问题
6.增加vip检测,StorePage增加了start

云天逵 пре 1 година
родитељ
комит
1b17002065

+ 1 - 1
android/app/build.gradle

@@ -21,7 +21,7 @@ android {
 
     defaultConfig {
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
-        applicationId = "com.example.clean.clean"
+        applicationId = "com.cleanpro.tools"
         // You can update the following values to match your application needs.
         // For more information, see: https://flutter.dev/to/review-gradle-config.
         minSdk = flutter.minSdkVersion

BIN
assets/anim/anim_deleting.zip


BIN
assets/anim/anim_fireworks.zip


+ 47 - 41
lib/base/base_photo_controller.dart

@@ -1,4 +1,9 @@
 import 'package:clean/base/base_controller.dart';
+import 'package:clean/data/repositories/user_repository.dart';
+import 'package:clean/dialog/photo_delete_finish_dialog.dart';
+import 'package:clean/dialog/photo_deleting_dialog.dart';
+import 'package:clean/module/store/store_view.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:get/get.dart';
 import 'package:wechat_assets_picker/wechat_assets_picker.dart';
 import 'package:clean/module/image_picker/image_picker_util.dart';
@@ -17,15 +22,14 @@ abstract class BasePhotoController extends BaseController {
   // 将selectedFilesSize转成String类型,然后单位转换,如果超过1MB,则转成MB,超过1GB,则转成GB,否则KB
   String get selectedFilesSizeString {
     if (selectedFilesSize.value > 1024) {
-      return "${(selectedFilesSize.value  / 1024).toStringAsFixed(2)}MB";
-    } else if (selectedFilesSize.value > 1024 *  1024) {
-      return "${(selectedFilesSize.value / 1024 /  1024).toStringAsFixed(2)}GB";
+      return "${(selectedFilesSize.value / 1024).toStringAsFixed(2)}MB";
+    } else if (selectedFilesSize.value > 1024 * 1024) {
+      return "${(selectedFilesSize.value / 1024 / 1024).toStringAsFixed(2)}GB";
     } else {
       return "${(selectedFilesSize.value).toStringAsFixed(2)}KB";
     }
   }
 
-
   // 获取选中的文件大小
   Future<void> getSelectedFilesSize() async {
     double totalSize = 0;
@@ -79,7 +83,6 @@ abstract class BasePhotoController extends BaseController {
     getSelectedFilesSize();
   }
 
-
   PhotoGroup getGroupByImages(List<AssetEntity> images) {
     final imageIds = images.map((img) => img.id).toSet();
     return photoGroups.firstWhere(
@@ -106,7 +109,6 @@ abstract class BasePhotoController extends BaseController {
     }
     selectedFileCount.value = selectedIds.length;
     if (selectedIds.isEmpty) {
-
       selectedFilesSize.value = 0;
       return;
     }
@@ -121,52 +123,58 @@ abstract class BasePhotoController extends BaseController {
     restoreSelections();
   }
 
-
-
   void loadPhotos();
 
   // 点击删除
   void clickDelete() async {
-
-    if (selectedPhotosIds.isNotEmpty) {
-      final assetsToDelete = photoGroups
-          .expand((group) => group.images
-              .where((asset) => selectedPhotosIds.contains(asset.id)))
-          .toList();
-
-      final List<String> result = await PhotoManager.editor.deleteWithIds(
-        assetsToDelete.map((e) => e.id).toList(),
-      );
-
-      if (result.length == selectedPhotosIds.length) {
-        for (var group in photoGroups) {
-          group.images
-              .removeWhere((element) => selectedPhotosIds.contains(element.id));
-
-
+    if (userRepository.isVip()) {
+      if (selectedPhotosIds.isNotEmpty) {
+        photoDeletingDialog();
+        final assetsToDelete = photoGroups
+            .expand((group) => group.images
+                .where((asset) => selectedPhotosIds.contains(asset.id)))
+            .toList();
+
+        final List<String> result = await PhotoManager.editor.deleteWithIds(
+          assetsToDelete.map((e) => e.id).toList(),
+        );
+
+        if (result.length == selectedPhotosIds.length) {
+          for (var group in photoGroups) {
+            group.images.removeWhere(
+                (element) => selectedPhotosIds.contains(element.id));
+          }
+          ImagePickerUtil.updatePhotoGroupDate(
+              getPhotosType(), selectedPhotosIds);
+
+          selectedPhotosIds.clear();
+          isDeleteFinish = true;
+
+          ToastUtil.show("Delete success");
+          Future.delayed(Duration(seconds: 2), () {
+            SmartDialog.dismiss(tag: 'photoDeletingDialog');
+            photoDeleteFinishDialog();
+          });
+        } else {
+          SmartDialog.dismiss(tag: 'photoDeletingDialog');
+          ToastUtil.show("Delete failed");
+          isDeleteFinish = false;
         }
-        ImagePickerUtil.updatePhotoGroupDate(
-            getPhotosType(), selectedPhotosIds);
-
-        selectedPhotosIds.clear();
-        isDeleteFinish = true;
-        ToastUtil.show("Delete success");
-
-      } else {
-        ToastUtil.show("Delete failed");
-        isDeleteFinish = false;
       }
+    } else {
+      StorePage.start();
     }
   }
 
   void updateSelections(Set<String> selectedIds) {
-
-    print("BasePhotoController updateSelections selectedIds $selectedIds, selectedPhotosIds $selectedPhotosIds getPhotosType() ${getPhotosType()}");
+    print(
+        "BasePhotoController updateSelections selectedIds $selectedIds, selectedPhotosIds $selectedPhotosIds getPhotosType() ${getPhotosType()}");
     // selectedId如果是selectedPhotosIds对象,那么selectedPhotosIds.assignAll(selectedIds)会清空
     // selectedPhotosIds.assignAll(selectedIds);
 
     selectedPhotosIds.assignAll(Set.from(selectedIds));
-    print("BasePhotoController updateSelections selectedIds $selectedIds, selectedPhotosIds $selectedPhotosIds getPhotosType() ${getPhotosType()}");
+    print(
+        "BasePhotoController updateSelections selectedIds $selectedIds, selectedPhotosIds $selectedPhotosIds getPhotosType() ${getPhotosType()}");
 
     switch (getPhotosType()) {
       case PhotosType.peoplePhotos:
@@ -186,11 +194,9 @@ abstract class BasePhotoController extends BaseController {
         ImagePickerUtil.selectedBlurryPhotosIds.assignAll(selectedPhotosIds);
         break;
     }
-
   }
 
-  PhotosType
-      getPhotosType();
+  PhotosType getPhotosType();
 
   // 将photoGroups中所有的图片返回
   List<AssetEntity> getAllPhotos() {

+ 13 - 0
lib/data/consts/constants.dart

@@ -25,6 +25,7 @@ class Constants {
 
   static const String isPolicyGranted = 'isPolicyGranted';
   static const String isIntro = 'isIntroduction';
+  static const String isFirstClickHomeClean = 'isFirstClickClean';
   static String baseUrl = getBaseUrl();
 
   static const String appDefaultChannel = "Android";
@@ -89,3 +90,15 @@ void setPrivacyPolicy(bool isAgree) {
 }
 
 
+bool isFirstClickHomeClean() {
+  return KVUtil.getBool(Constants.isFirstClickHomeClean, true);
+}
+void setFirstClickClean(bool isFirst) {
+  KVUtil.putBool(Constants.isFirstClickHomeClean, isFirst);
+}
+
+
+
+
+
+

+ 14 - 10
lib/dialog/photo_delete_finish_dialog.dart

@@ -9,13 +9,16 @@ import 'package:lottie/lottie.dart';
 import 'package:get/get.dart';
 import 'package:clean/router/app_pages.dart';
 
-void photoDeleteFinishDialog() {
+void photoDeleteFinishDialog( { Function? onDismiss}) {
   const tag = 'photoDeleteFinishDialog';
   // 300ms后关闭弹窗
-  Future.delayed(Duration(milliseconds: 1500), () {
+  Future.delayed(Duration(seconds: 3), () {
     SmartDialog.dismiss(tag: tag);
-    //   回到首页
-    Get.until((route) => Get. currentRoute ==RoutePath.mainTab);
+    if (onDismiss != null) {
+      onDismiss();
+    }
+    // //   回到首页
+    // Get.until((route) => Get. currentRoute ==RoutePath.mainTab);
 
 
   });
@@ -31,7 +34,7 @@ void photoDeleteFinishDialog() {
           child: Stack(
             children: [
               Positioned(
-                  top: 176.h,
+
                   left: 0,
                   right: 0,
                   child: Column(
@@ -40,9 +43,10 @@ void photoDeleteFinishDialog() {
                         crossAxisAlignment: CrossAxisAlignment.center,
                         mainAxisAlignment: MainAxisAlignment.center,
                         children: [
-                          Assets.images.iconPreviewSwiperEndFinish.image(
-                            width: 180.w,
-                            height: 180.w,
+                          Lottie.asset(
+                            Assets.anim.animFireworks,
+                            height: 351.h,
+                            repeat: false,
                           ),
                           Text(
                             'Perfect!',
@@ -97,9 +101,9 @@ void photoDeleteFinishDialog() {
                     ],
                   )),
               IgnorePointer(
-                child: Assets.images.bgPreviewSwiperEndFirework.image(
+                child: Assets.images.bgPhotoSelectedPreviewFinish.image(
                   width: 360.w,
-                  height: 234.h,
+                  height: 335.h,
                 ),
               ),
             ],

+ 9 - 4
lib/dialog/photo_deleting_dialog.dart

@@ -22,14 +22,19 @@ void photoDeletingDialog() {
           child: Stack(
             children: [
               Positioned(
-                top: 176.h,
+                top: 100.h,
                 left: 0,
                 right: 0,
                 child: Column(
                   children: [
-                    Assets.images.iconPhotoDeleting
-                        .image(width: 273.w, height: 220.h),
-                    SizedBox(height: 45.h),
+                    Lottie.asset(
+                      Assets.anim.animDeleting,
+                      height: 300.w,
+                      repeat: true,
+                    ),
+                    // Assets.images.iconPhotoDeleting
+                    //     .image(width: 273.w, height: 220.h),
+                    // SizedBox(height: 45.h),
                     Text(
                       'Deleting...',
                       style: TextStyle(

+ 4 - 1
lib/dialog/photo_preview_tip_dialog.dart

@@ -10,7 +10,10 @@ void photoPreviewTipDialog({VoidCallback? clickCallback}) {
   SmartDialog.show(
       tag: tag,
       backType: SmartBackType.block,
-      clickMaskDismiss: false,
+      onDismiss: () {
+        clickCallback?.call();
+      },
+      clickMaskDismiss: true,
       maskColor: ColorName.black70,
       builder: (_) {
         return SizedBox(

+ 118 - 74
lib/module/photo_preview/photo_preview_controller.dart

@@ -4,6 +4,7 @@ import 'package:clean/base/base_controller.dart';
 import 'package:clean/base/base_photo_controller.dart';
 import 'package:clean/data/bean/photos_type.dart';
 import 'package:clean/data/consts/constants.dart';
+import 'package:clean/data/repositories/user_repository.dart';
 import 'package:clean/dialog/photo_delete_finish_dialog.dart';
 import 'package:clean/dialog/photo_deleting_dialog.dart';
 import 'package:clean/dialog/photo_preview_tip_dialog.dart';
@@ -14,14 +15,18 @@ import 'package:clean/module/people_photo/photo_group.dart';
 import 'package:clean/module/photo_preview/phtoto_selected_preview_view.dart';
 import 'package:clean/module/screenshots_blurry/screenshots_controller.dart';
 import 'package:clean/module/similar_photo/similar_photo_controller.dart';
+import 'package:clean/module/store/store_view.dart';
 import 'package:clean/utils/file_utils.dart';
 import 'package:clean/utils/toast_util.dart';
 import 'package:flutter/Material.dart';
 import 'package:flutter_card_swiper/flutter_card_swiper.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:get/get.dart';
+import 'package:lottie/lottie.dart';
 import 'package:wechat_assets_picker/wechat_assets_picker.dart';
 
-class PhotoPreviewController extends BaseController {
+class PhotoPreviewController extends BaseController
+    with GetSingleTickerProviderStateMixin {
   Rx<CardSwiperController> cardSwiperController = CardSwiperController().obs;
   final RxList<AssetEntity> listAssetEntity = <AssetEntity>[].obs;
   final RxSet<String> selectedPhotosIds = <String>{}.obs;
@@ -34,10 +39,16 @@ class PhotoPreviewController extends BaseController {
   RxDouble selectedFilesSize = 0.0.obs;
   RxInt selectedFileCount = 0.obs;
 
+  late AnimationController animationController;
+  RxBool animationIsComplete = false.obs;
+
   @override
   void onInit() {
     super.onInit();
-
+    animationController = AnimationController(
+      vsync: this,
+      duration: const Duration(seconds: 3),
+    );
     print('PhotoPreviewController onInit');
     _getArgs(); // 获取传递的参数
     _initData(); // 初始化数据
@@ -54,19 +65,33 @@ class PhotoPreviewController extends BaseController {
         }
       }
     });
+
+    animationController.addStatusListener((status) {
+      if (status == AnimationStatus.forward) {
+        //   延迟一秒
+        Future.delayed(Duration(seconds: 1), () {
+          animationIsComplete.value = true;
+        });
+      }
+      if (status == AnimationStatus.completed) {
+        Future.delayed(Duration(seconds: 1), () {
+          PhotoSelectedPreviewPage.start(photosType, selectedPhotosIds);
+        });
+      }
+    });
   }
 
   @override
   void onReady() {
     // TODO: implement onReady
     super.onReady();
+
     print('PhotoPreviewController onReady');
     if (isFirstOpenPhotoPreview()) {
       photoPreviewTipDialog(clickCallback: () {
         setFirstOpenPhotoPreview(false);
       });
     }
-
   }
 
   // 获取参数
@@ -177,13 +202,15 @@ class PhotoPreviewController extends BaseController {
   }
 
   void updateSelections(Set<String> selectedIds) {
-    print('PhotoPreviewController updateSelections photosType $photosType selectedIds $selectedIds');
+    print(
+        'PhotoPreviewController updateSelections photosType $photosType selectedIds $selectedIds');
     selectedPhotosIds.assignAll(selectedIds);
 
     restoreSelections();
   }
 
   void recoverSelectPhoto() {
+    print('PhotoPreviewController recoverSelectPhoto');
     cardSwiperController.value.undo();
   }
 
@@ -194,6 +221,7 @@ class PhotoPreviewController extends BaseController {
   @override
   void onClose() {
     print('PhotoPreviewController onClose');
+    animationController.dispose();
     super.onClose();
     isSwiperEnd.value = false;
 
@@ -206,7 +234,6 @@ class PhotoPreviewController extends BaseController {
     Get.back();
   }
 
-
   // // 保存选择的图片ID列表
   void _saveSelectedPhotos(PhotosType type) {
     switch (type) {
@@ -302,89 +329,106 @@ class PhotoPreviewController extends BaseController {
     isSwiperEnd.value = true;
     print('onSwiperEnd');
 
-    PhotoSelectedPreviewPage.start(photosType, selectedPhotosIds);
+    // 延迟500ms
+    Future.delayed(Duration(milliseconds: 200), () {
+      animationController.forward(from: 0);
+    });
+
+    // PhotoSelectedPreviewPage.start(photosType, selectedPhotosIds);
   }
 
   clickDelete() async {
     print('clickDelete');
 
-    switch (photosType) {
-      case PhotosType.peoplePhotos:
-        ImagePickerUtil.selectedPeoplePhotosIds.assignAll(selectedPhotosIds);
-        break;
-      case PhotosType.screenshots:
-        ImagePickerUtil.selectedScreenshotPhotosIds
-            .assignAll(selectedPhotosIds);
-        break;
-      case PhotosType.similarPhotos:
-        ImagePickerUtil.selectedSimilarPhotosIds.assignAll(selectedPhotosIds);
-        break;
-      case PhotosType.locationPhotos:
-        ImagePickerUtil.selectedLocationPhotosIds.assignAll(selectedPhotosIds);
-        break;
-      case PhotosType.blurryPhotos:
-        ImagePickerUtil.selectedBlurryPhotosIds.assignAll(selectedPhotosIds);
-        break;
-    }
-    if (selectedPhotosIds.isNotEmpty) {
-      // 获取要删除的资产
-      final assetsToDelete = listAssetEntity
-          .where(
-            (asset) => selectedPhotosIds.contains(asset.id),
-          )
-          .toList();
-
-      // 调用方法会返回被删除的资源,如果全部失败会返回空列表。
-      final List<String> result = await PhotoManager.editor.deleteWithIds(
-        assetsToDelete.map((e) => e.id).toList(),
-      );
-      print('PhotoPreviewController result $result');
-
-      //   比较result和selectedPhotosIds,如果result和selectedPhotosIds相等,说明删除成功,走下面的逻辑
-      // 如果不相等,说明有删除失败的,走else逻辑
-      if (result.length == selectedPhotosIds.length) {
-        switch (photosType) {
-          case PhotosType.peoplePhotos:
-            ImagePickerUtil.updatePhotoGroupDate(
-                photosType, ImagePickerUtil.selectedPeoplePhotosIds);
-
-            break;
-          case PhotosType.screenshots:
-            ImagePickerUtil.updatePhotoGroupDate(
-                photosType, ImagePickerUtil.selectedScreenshotPhotosIds);
-
-            break;
-          case PhotosType.similarPhotos:
-            ImagePickerUtil.updatePhotoGroupDate(
-                photosType, ImagePickerUtil.selectedSimilarPhotosIds);
-
-            break;
-          case PhotosType.locationPhotos:
-            ImagePickerUtil.updatePhotoGroupDate(
-                photosType, ImagePickerUtil.selectedLocationPhotosIds);
-
-            break;
-          case PhotosType.blurryPhotos:
-            ImagePickerUtil.updatePhotoGroupDate(
-                photosType, ImagePickerUtil.selectedBlurryPhotosIds);
+    if (userRepository.isVip()) {
+      switch (photosType) {
+        case PhotosType.peoplePhotos:
+          ImagePickerUtil.selectedPeoplePhotosIds.assignAll(selectedPhotosIds);
+          break;
+        case PhotosType.screenshots:
+          ImagePickerUtil.selectedScreenshotPhotosIds
+              .assignAll(selectedPhotosIds);
+          break;
+        case PhotosType.similarPhotos:
+          ImagePickerUtil.selectedSimilarPhotosIds.assignAll(selectedPhotosIds);
+          break;
+        case PhotosType.locationPhotos:
+          ImagePickerUtil.selectedLocationPhotosIds
+              .assignAll(selectedPhotosIds);
+          break;
+        case PhotosType.blurryPhotos:
+          ImagePickerUtil.selectedBlurryPhotosIds.assignAll(selectedPhotosIds);
+          break;
+      }
+      if (selectedPhotosIds.isNotEmpty) {
+        photoDeletingDialog();
+        // 获取要删除的资产
+        final assetsToDelete = listAssetEntity
+            .where(
+              (asset) => selectedPhotosIds.contains(asset.id),
+            )
+            .toList();
+
+        // 调用方法会返回被删除的资源,如果全部失败会返回空列表。
+        final List<String> result = await PhotoManager.editor.deleteWithIds(
+          assetsToDelete.map((e) => e.id).toList(),
+        );
+        print('PhotoPreviewController result $result');
+
+        //   比较result和selectedPhotosIds,如果result和selectedPhotosIds相等,说明删除成功,走下面的逻辑
+        // 如果不相等,说明有删除失败的,走else逻辑
+        if (result.length == selectedPhotosIds.length) {
+          switch (photosType) {
+            case PhotosType.peoplePhotos:
+              ImagePickerUtil.updatePhotoGroupDate(
+                  photosType, ImagePickerUtil.selectedPeoplePhotosIds);
+
+              break;
+            case PhotosType.screenshots:
+              ImagePickerUtil.updatePhotoGroupDate(
+                  photosType, ImagePickerUtil.selectedScreenshotPhotosIds);
+
+              break;
+            case PhotosType.similarPhotos:
+              ImagePickerUtil.updatePhotoGroupDate(
+                  photosType, ImagePickerUtil.selectedSimilarPhotosIds);
+
+              break;
+            case PhotosType.locationPhotos:
+              ImagePickerUtil.updatePhotoGroupDate(
+                  photosType, ImagePickerUtil.selectedLocationPhotosIds);
+
+              break;
+            case PhotosType.blurryPhotos:
+              ImagePickerUtil.updatePhotoGroupDate(
+                  photosType, ImagePickerUtil.selectedBlurryPhotosIds);
+
+              break;
+          }
 
-            break;
+          cleanSelections();
+          ToastUtil.show('Delete success');
+          Future.delayed(Duration(seconds: 2), () {
+            SmartDialog.dismiss(tag: 'photoDeletingDialog');
+            photoDeleteFinishDialog();
+          });
+        } else {
+          SmartDialog.dismiss(tag: 'photoDeletingDialog');
+          // 删除失败
+          ToastUtil.show("Delete failed");
         }
-
-        cleanSelections();
-      } else {
-        // 删除失败
-        ToastUtil.show("Delete failed");
       }
+    } else {
+      StorePage.start();
     }
   }
 
   // 将selectedFilesSize转成String类型,然后单位转换,如果超过1MB,则转成MB,超过1GB,则转成GB,否则KB
   String get selectedFilesSizeString {
     if (selectedFilesSize.value > 1024) {
-      return "${(selectedFilesSize.value  / 1024).toStringAsFixed(2)}MB";
-    } else if (selectedFilesSize.value > 1024 *  1024) {
-      return "${(selectedFilesSize.value / 1024 /  1024).toStringAsFixed(2)}GB";
+      return "${(selectedFilesSize.value / 1024).toStringAsFixed(2)}MB";
+    } else if (selectedFilesSize.value > 1024 * 1024) {
+      return "${(selectedFilesSize.value / 1024 / 1024).toStringAsFixed(2)}GB";
     } else {
       return "${(selectedFilesSize.value).toStringAsFixed(2)}KB";
     }

+ 133 - 124
lib/module/photo_preview/photo_preview_view.dart

@@ -8,6 +8,7 @@ import 'package:flutter/src/widgets/framework.dart';
 import 'package:flutter_card_swiper/flutter_card_swiper.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:get/get.dart';
+import 'package:lottie/lottie.dart';
 import 'package:wechat_assets_picker/wechat_assets_picker.dart';
 
 class PhotoPreviewPage extends BasePage<PhotoPreviewController> {
@@ -72,22 +73,21 @@ class PhotoPreviewPage extends BasePage<PhotoPreviewController> {
                         onSwipe: controller.onSwipe,
                         onUndo: controller.onSwiperUndo,
                         numberOfCardsDisplayed:
-                            (controller.listAssetEntity.length == 1)
-                                ? 1
-                                : (controller.listAssetEntity.length == 2)
-                                    ? 2
-                                    : 3,
+                            (controller.listAssetEntity.length == 1) ? 1 : 2,
                         onEnd: controller.onSwiperEnd,
                         cardBuilder: (context,
                             index,
                             horizontalOffsetPercentage,
                             verticalOffsetPercentage) {
                           final assetEntity = controller.listAssetEntity[index];
-                          return AssetEntityImage(
-                            assetEntity,
-                            width: 314.w,
-                            height: 392.h,
-                            fit: BoxFit.cover,
+                          return ClipRRect(
+                            borderRadius: BorderRadius.circular(20.r),
+                            child: AssetEntityImage(
+                              assetEntity,
+                              width: 314.w,
+                              height: 392.h,
+                              fit: BoxFit.cover,
+                            ),
                           );
                         },
                       ),
@@ -103,11 +103,12 @@ class PhotoPreviewPage extends BasePage<PhotoPreviewController> {
         ),
       )),
       Obx(() {
-        if (controller.isSwiperEnd.value || controller.listAssetEntity.isEmpty) {
+        if (controller.isSwiperEnd.value ||
+            controller.listAssetEntity.isEmpty) {
           return IgnorePointer(
-            child: Assets.images.bgPreviewSwiperEndFirework.image(
+            child: Assets.images.bgPhotoSelectedPreviewFinish.image(
               width: 360.w,
-              height: 234.h,
+              height: 335.h,
             ),
           );
         } else {
@@ -178,67 +179,66 @@ class PhotoPreviewPage extends BasePage<PhotoPreviewController> {
   }
 
   Widget _bottomBarCard() {
-    return GestureDetector(
-      onTap: controller.clickDelete,
-      child: Container(
-        width: 360.w,
-        height: 81.h,
-        padding: EdgeInsets.symmetric(horizontal: 16.w),
-        decoration: ShapeDecoration(
-          color: Color(0xFF23232A),
-          shape: RoundedRectangleBorder(
-            side: BorderSide(
-                width: 1.w, color: Colors.white.withValues(alpha: 0.1)),
-            borderRadius: BorderRadius.only(
-              topLeft: Radius.circular(14.r),
-              topRight: Radius.circular(14.r),
-            ),
+    return Container(
+      width: 360.w,
+      height: 81.h,
+      padding: EdgeInsets.symmetric(horizontal: 16.w),
+      decoration: ShapeDecoration(
+        color: Color(0xFF23232A),
+        shape: RoundedRectangleBorder(
+          side: BorderSide(
+              width: 1.w, color: Colors.white.withValues(alpha: 0.1)),
+          borderRadius: BorderRadius.only(
+            topLeft: Radius.circular(14.r),
+            topRight: Radius.circular(14.r),
           ),
         ),
-        child: Row(
-          mainAxisAlignment: MainAxisAlignment.spaceBetween,
-          children: [
-            Obx(() {
-              return Text(
-                '${controller.selectedFileCount.value} files selected (${controller.selectedFilesSizeString} )',
-                textAlign: TextAlign.center,
-                style: TextStyle(
-                  color: Colors.white.withValues(alpha: 0.9),
-                  fontSize: 13.sp,
-                  fontWeight: FontWeight.w500,
-                ),
-              );
-            }),
-            Container(
-              width: 108.w,
-              height: 38.h,
-              decoration: ShapeDecoration(
-                color: Color(0xFF0279FB),
-                shape: RoundedRectangleBorder(
-                  borderRadius: BorderRadius.circular(10.r),
-                ),
+      ),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: [
+          Obx(() {
+            return Text(
+              '${controller.selectedFileCount.value} files selected (${controller.selectedFilesSizeString} )',
+              textAlign: TextAlign.center,
+              style: TextStyle(
+                color: Colors.white.withValues(alpha: 0.9),
+                fontSize: 13.sp,
+                fontWeight: FontWeight.w500,
               ),
-              child: Row(
-                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
-                children: [
-                  Text(
-                    'Delete',
-                    textAlign: TextAlign.center,
-                    style: TextStyle(
-                      color: Colors.white,
-                      fontSize: 16.sp,
-                      fontWeight: FontWeight.w500,
-                    ),
-                  ),
-                  Assets.images.iconDelete.image(
-                    width: 18.w,
-                    height: 18.h,
+            );
+          }),
+          GestureDetector(
+              onTap: controller.clickDelete,
+              child: Container(
+                width: 108.w,
+                height: 38.h,
+                decoration: ShapeDecoration(
+                  color: Color(0xFF0279FB),
+                  shape: RoundedRectangleBorder(
+                    borderRadius: BorderRadius.circular(10.r),
                   ),
-                ],
-              ),
-            ),
-          ],
-        ),
+                ),
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                  children: [
+                    Text(
+                      'Delete',
+                      textAlign: TextAlign.center,
+                      style: TextStyle(
+                        color: Colors.white,
+                        fontSize: 16.sp,
+                        fontWeight: FontWeight.w500,
+                      ),
+                    ),
+                    Assets.images.iconDelete.image(
+                      width: 18.w,
+                      height: 18.h,
+                    ),
+                  ],
+                ),
+              )),
+        ],
       ),
     );
   }
@@ -271,64 +271,73 @@ class PhotoPreviewPage extends BasePage<PhotoPreviewController> {
   Widget onSwiperEndCard() {
     return Column(
       children: [
-        Spacer(flex: 3,),
-        Center(
-          child: Column(
-            crossAxisAlignment: CrossAxisAlignment.center,
-            mainAxisAlignment: MainAxisAlignment.center,
-            children: [
-              Assets.images.iconPreviewSwiperEndFinish.image(
-                width: 180.w,
-                height: 180.w,
-              ),
-              Text(
-                'Perfect!',
-                textAlign: TextAlign.center,
-                style: TextStyle(
-                  color: Colors.white,
-                  fontSize: 32.sp,
-                  fontWeight: FontWeight.w700,
-                ),
-              ),
-              SizedBox(height: 16.h),
-              SizedBox(
-                child: Row(
-                  mainAxisAlignment: MainAxisAlignment.center,
-                  children: [
-                    Container(
-                      clipBehavior: Clip.antiAlias,
-                      decoration: BoxDecoration(),
-                      child: Assets.images.iconPreviewSwiperEndFirework.image(
-                        width: 40.w,
-                        height: 40.w,
-                      ),
-                    ),
-                    SizedBox(width: 4.w),
-                    Text(
-                      'All Similar and Redundant\nPhotos Cleared',
-                      textAlign: TextAlign.center,
-                      style: TextStyle(
-                        color: Colors.white.withValues(alpha: 0.9),
-                        fontSize: 16.sp,
-                        fontWeight: FontWeight.w400,
-                      ),
+        Container(
+          child: Lottie.asset(
+            Assets.anim.animFireworks,
+            controller: controller.animationController,
+            height: 351.h,
+            repeat: false,
+          ),
+        ),
+        Visibility(
+            visible: controller.animationIsComplete.value,
+            child: Center(
+              child: Column(
+                crossAxisAlignment: CrossAxisAlignment.center,
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  Text(
+                    'Perfect!',
+                    textAlign: TextAlign.center,
+                    style: TextStyle(
+                      color: Colors.white,
+                      fontSize: 32.sp,
+                      fontWeight: FontWeight.w700,
                     ),
-                    SizedBox(width: 4.w),
-                    Container(
-                      clipBehavior: Clip.antiAlias,
-                      decoration: BoxDecoration(),
-                      child: Assets.images.iconPreviewSwiperEndFirework.image(
-                        width: 40.w,
-                        height: 40.w,
-                      ),
+                  ),
+                  SizedBox(height: 16.h),
+                  SizedBox(
+                    child: Row(
+                      mainAxisAlignment: MainAxisAlignment.center,
+                      children: [
+                        Container(
+                          clipBehavior: Clip.antiAlias,
+                          decoration: BoxDecoration(),
+                          child:
+                              Assets.images.iconPreviewSwiperEndFirework.image(
+                            width: 40.w,
+                            height: 40.w,
+                          ),
+                        ),
+                        SizedBox(width: 4.w),
+                        Text(
+                          'All Similar and Redundant\nPhotos Cleared',
+                          textAlign: TextAlign.center,
+                          style: TextStyle(
+                            color: Colors.white.withValues(alpha: 0.9),
+                            fontSize: 16.sp,
+                            fontWeight: FontWeight.w400,
+                          ),
+                        ),
+                        SizedBox(width: 4.w),
+                        Container(
+                          clipBehavior: Clip.antiAlias,
+                          decoration: BoxDecoration(),
+                          child:
+                              Assets.images.iconPreviewSwiperEndFirework.image(
+                            width: 40.w,
+                            height: 40.w,
+                          ),
+                        ),
+                      ],
                     ),
-                  ],
-                ),
+                  ),
+                ],
               ),
-            ],
-          ),
+            )),
+        Spacer(
+          flex: 5,
         ),
-        Spacer(flex: 5,),
         controller.listAssetEntity.isEmpty ? SizedBox() : _bottomBarCard(),
       ],
     );

+ 58 - 49
lib/module/photo_preview/photo_selected_preview_controller.dart

@@ -1,11 +1,14 @@
 import 'package:clean/base/base_controller.dart';
 import 'package:clean/base/base_photo_controller.dart';
 import 'package:clean/data/bean/photos_type.dart';
+import 'package:clean/data/repositories/user_repository.dart';
 import 'package:clean/dialog/photo_delete_finish_dialog.dart';
 import 'package:clean/dialog/photo_deleting_dialog.dart';
 import 'package:clean/module/image_picker/image_picker_util.dart';
 import 'package:clean/module/people_photo/photo_group.dart';
 import 'package:clean/module/photo_preview/photo_preview_controller.dart';
+import 'package:clean/module/store/store_view.dart';
+import 'package:clean/router/app_pages.dart';
 import 'package:clean/utils/toast_util.dart';
 import 'package:flutter/Material.dart';
 import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -68,58 +71,64 @@ class PhotoSelectedPreviewController extends BasePhotoController {
   @override
   void clickDelete() async {
 
-    photoDeletingDialog();
-    if (selectedPhotosIds.isNotEmpty) {
-      print("PhotoSelectedPreviewController clickDelete selectedPhotosIds $selectedPhotosIds");
-      final assetsToDelete = photoGroups
-          .expand((group) => group.images
-          .where((asset) => selectedPhotosIds.contains(asset.id)))
-          .toList();
-
-      final List<String> result = await PhotoManager.editor.deleteWithIds(
-        assetsToDelete.map((e) => e.id).toList(),
-      );
-
-      if (result.length == selectedPhotosIds.length) {
-
-        isDeleteFinish = true;
-
-        for (var group in photoGroups) {
-          group.images
-              .removeWhere((element) => selectedPhotosIds.contains(element.id));
-
-          // 更新 group 中的 selectedImages 状态
-          group.selectedImages.value = group.images.map((image) {
-            return selectedPhotosIds.contains(image.id);
-          }).toList();
-
-          // 更新 group.isSelected
-          group.isSelected.value = group.selectedImages.every((selected) => selected);
-        }
-
-        photoPreviewController.selectedPhotosIds.assignAll(selectedPhotosIds);
-        photoPreviewController.cleanSelections();
-
-        ImagePickerUtil.updatePhotoGroupDate(
-            getPhotosType(), selectedPhotosIds);
-        selectedPhotosIds.clear();
-        ToastUtil.show("Delete success selectedPhotosIds ");
-
-
-
-        Future.delayed(Duration(seconds: 2), () {
-
+    if(userRepository.isVip()) {
+      if (selectedPhotosIds.isNotEmpty) {
+        photoDeletingDialog();
+        print(
+            "PhotoSelectedPreviewController clickDelete selectedPhotosIds $selectedPhotosIds");
+        final assetsToDelete = photoGroups
+            .expand((group) =>
+            group.images
+                .where((asset) => selectedPhotosIds.contains(asset.id)))
+            .toList();
+
+        final List<String> result = await PhotoManager.editor.deleteWithIds(
+          assetsToDelete.map((e) => e.id).toList(),
+        );
+
+        if (result.length == selectedPhotosIds.length) {
+          isDeleteFinish = true;
+
+          for (var group in photoGroups) {
+            group.images
+                .removeWhere((element) =>
+                selectedPhotosIds.contains(element.id));
+
+            // 更新 group 中的 selectedImages 状态
+            group.selectedImages.value = group.images.map((image) {
+              return selectedPhotosIds.contains(image.id);
+            }).toList();
+
+            // 更新 group.isSelected
+            group.isSelected.value =
+                group.selectedImages.every((selected) => selected);
+          }
+
+          photoPreviewController.selectedPhotosIds.assignAll(selectedPhotosIds);
+          photoPreviewController.cleanSelections();
+
+          ImagePickerUtil.updatePhotoGroupDate(
+              getPhotosType(), selectedPhotosIds);
+          selectedPhotosIds.clear();
+          ToastUtil.show("Delete success");
+
+
+          Future.delayed(Duration(seconds: 2), () {
+            SmartDialog.dismiss(tag: 'photoDeletingDialog');
+            photoDeleteFinishDialog(onDismiss: () {
+              Get.until((route) => Get. currentRoute ==RoutePath.mainTab);
+            });
+          });
+        } else {
           SmartDialog.dismiss(tag: 'photoDeletingDialog');
-          photoDeleteFinishDialog();
-        });
-
+          ToastUtil.show("Delete failed");
+          isDeleteFinish = false;
+        }
       } else {
-        ToastUtil.show("Delete failed");
-        isDeleteFinish = false;
+        isKeepAll.value = true;
       }
-    }else {
-      // ToastUtil.show("Please select the photo to delete");
-      isKeepAll.value = true;
+    }else{
+      StorePage.start();
     }
   }
   @override

+ 72 - 58
lib/module/similar_photo/similar_photo_controller.dart

@@ -3,10 +3,15 @@ import 'dart:io';
 import 'package:clean/base/base_controller.dart';
 import 'package:clean/base/base_photo_controller.dart';
 import 'package:clean/data/bean/photos_type.dart';
+import 'package:clean/data/repositories/user_repository.dart';
+import 'package:clean/dialog/photo_delete_finish_dialog.dart';
+import 'package:clean/dialog/photo_deleting_dialog.dart';
 import 'package:clean/module/image_picker/image_picker_util.dart';
 import 'package:clean/module/people_photo/photo_group.dart';
 import 'package:clean/module/photo_preview/photo_preview_view.dart';
+import 'package:clean/module/store/store_view.dart';
 import 'package:clean/utils/toast_util.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:get/get.dart';
 
 import 'package:wechat_assets_picker/wechat_assets_picker.dart';
@@ -34,72 +39,81 @@ class SimilarPhotoController extends BasePhotoController {
     ImagePickerUtil.selectedSimilarPhotosIds.assignAll(selectedPhotosIds);
   }
 
-
-
   // 点击删除单组选中的图片
   void clickSingleGroupDelete(List<AssetEntity> images) async {
-    // 获取指定组
-    final group = getGroupByImages(images);
-
-    // 获取该组中所有选中的图片
-    final selectedImages = group.images
-        .where((image) => group.selectedImages[group.images.indexOf(image)])
-        .toList();
-
-    if (selectedImages.isEmpty) {
-      ToastUtil.show('Please select the photo to delete');
-      return;
-    }
-
-    try {
-      // 执行删除操作
-      final List<String> result = await PhotoManager.editor.deleteWithIds(
-        selectedImages.map((e) => e.id).toList(),
-      );
-
-      // 检查删除成功的图片数量
-      if (result.length == selectedImages.length) {
-        // 先删除该组的图片,再更新selectedImages和selectedPhotosIds
-        List<AssetEntity> imagesToRemove = [];
-        List<int> indexesToRemove = [];
-
-        for (var image in selectedImages) {
-          // 找到图片在该组中的索引
-          int index = group.images.indexOf(image);
-          imagesToRemove.add(image);
-          indexesToRemove.add(index);
-        }
-
-        // 从后往前删除,避免删除后索引错位
-        for (int i = indexesToRemove.length - 1; i >= 0; i--) {
-          int index = indexesToRemove[i];
-          group.images.removeAt(index);
-          group.selectedImages.removeAt(index);
-        }
-
-        // 如果当前组为空,则删除该组
-        if (group.images.isEmpty) {
-          photoGroups.remove(group);
-        }
+    if (userRepository.isVip()) {
+      // 获取指定组
+      final group = getGroupByImages(images);
+
+      // 获取该组中所有选中的图片
+      final selectedImages = group.images
+          .where((image) => group.selectedImages[group.images.indexOf(image)])
+          .toList();
+
+      if (selectedImages.isEmpty) {
+        ToastUtil.show('Please select the photo to delete');
+        return;
+      }
 
-        // 更新选中的图片ID
-        for (var image in imagesToRemove) {
-          updateSelectedPhotosIds(image.id, false);
+      photoDeletingDialog();
+      try {
+        // 执行删除操作
+        final List<String> result = await PhotoManager.editor.deleteWithIds(
+          selectedImages.map((e) => e.id).toList(),
+        );
+
+        // 检查删除成功的图片数量
+        if (result.length == selectedImages.length) {
+          // 先删除该组的图片,再更新selectedImages和selectedPhotosIds
+          List<AssetEntity> imagesToRemove = [];
+          List<int> indexesToRemove = [];
+
+          for (var image in selectedImages) {
+            // 找到图片在该组中的索引
+            int index = group.images.indexOf(image);
+            imagesToRemove.add(image);
+            indexesToRemove.add(index);
+          }
+
+          // 从后往前删除,避免删除后索引错位
+          for (int i = indexesToRemove.length - 1; i >= 0; i--) {
+            int index = indexesToRemove[i];
+            group.images.removeAt(index);
+            group.selectedImages.removeAt(index);
+          }
+
+          // 如果当前组为空,则删除该组
+          if (group.images.isEmpty) {
+            photoGroups.remove(group);
+          }
+
+          // 更新选中的图片ID
+          for (var image in imagesToRemove) {
+            updateSelectedPhotosIds(image.id, false);
+          }
+          selectedFileCount.value = selectedPhotosIds.length;
+          getSelectedFilesSize();
+
+          Future.delayed(Duration(seconds: 2), () {
+            SmartDialog.dismiss(tag: 'photoDeletingDialog');
+            photoDeleteFinishDialog();
+          });
+
+          ToastUtil.show("Delete success");
+        } else {
+          SmartDialog.dismiss(tag: 'photoDeletingDialog');
+          ToastUtil.show("Delete failed");
         }
-
-
-
-        ToastUtil.show("Delete success");
-      } else {
-        ToastUtil.show("Delete failed");
+      } catch (e) {
+        // 异常处理
+        SmartDialog.dismiss(tag: 'photoDeletingDialog');
+        ToastUtil.show("Error occurred during deletion: $e");
       }
-    } catch (e) {
-      // 异常处理
-      ToastUtil.show("Error occurred during deletion: $e");
+    } else {
+      StorePage.start();
     }
   }
 
-
   @override
   PhotosType getPhotosType() => PhotosType.similarPhotos;
 }

+ 5 - 0
lib/module/store/store_view.dart

@@ -2,6 +2,7 @@ import 'package:clean/base/base_page.dart';
 import 'package:clean/data/bean/store_item.dart';
 import 'package:clean/module/browser/browser_view.dart';
 import 'package:clean/module/store/store_controller.dart';
+import 'package:clean/router/app_pages.dart';
 import 'package:clean/utils/expand.dart';
 import 'package:flutter/Material.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
@@ -14,6 +15,10 @@ import '../../resource/assets.gen.dart';
 class StorePage extends BasePage<StoreController> {
   const StorePage({super.key});
 
+  static start() {
+    Get.toNamed(RoutePath.store);
+  }
+
   @override
   bool immersive() {
     return true;

+ 2 - 0
lib/module/wallpaper/wallpaper_view.dart

@@ -100,6 +100,8 @@ class WallPaperPage extends BasePage<WallPaperController> {
                                                   .wallpapersList[index]
                                                   .thumbUrl ??
                                               "",
+                                          errorWidget: (context, url, error) =>
+                                              Icon(Icons.error),
                                           fit: BoxFit.cover,
                                         ),
                                       ));