Răsfoiți Sursa

整体页面元素和接口开发完毕,下一步开发播放下载等逻辑

100Years 1 lună în urmă
părinte
comite
4434ac19ae
43 a modificat fișierele cu 1335 adăugiri și 83 ștergeri
  1. 103 1
      AIRingtone.xcodeproj/project.pbxproj
  2. 22 0
      AIRingtone/Assets.xcassets/Common/gary_right_arrow.imageset/Contents.json
  3. BIN
      AIRingtone/Assets.xcassets/Common/gary_right_arrow.imageset/gary_right_arrow@2x.png
  4. BIN
      AIRingtone/Assets.xcassets/Common/gary_right_arrow.imageset/gary_right_arrow@3x.png
  5. BIN
      AIRingtone/Assets.xcassets/Common/navi_back_white.imageset/navi_back_white@2x.png
  6. BIN
      AIRingtone/Assets.xcassets/Common/navi_back_white.imageset/navi_back_white@3x.png
  7. 22 0
      AIRingtone/Assets.xcassets/Common/ring_folder.imageset/Contents.json
  8. BIN
      AIRingtone/Assets.xcassets/Common/ring_folder.imageset/ring_folder@2x.png
  9. BIN
      AIRingtone/Assets.xcassets/Common/ring_folder.imageset/ring_folder@3x.png
  10. 22 0
      AIRingtone/Assets.xcassets/Common/ring_null.imageset/Contents.json
  11. BIN
      AIRingtone/Assets.xcassets/Common/ring_null.imageset/ring_null@2x.png
  12. BIN
      AIRingtone/Assets.xcassets/Common/ring_null.imageset/ring_null@3x.png
  13. 6 0
      AIRingtone/Assets.xcassets/Discover/Contents.json
  14. 22 0
      AIRingtone/Assets.xcassets/Discover/nav_title_discover.imageset/Contents.json
  15. BIN
      AIRingtone/Assets.xcassets/Discover/nav_title_discover.imageset/nav_title_discover@2x.png
  16. BIN
      AIRingtone/Assets.xcassets/Discover/nav_title_discover.imageset/nav_title_discover@3x.png
  17. 6 0
      AIRingtone/Assets.xcassets/Launch/Contents.json
  18. 22 0
      AIRingtone/Assets.xcassets/Launch/launch_rightArrow.imageset/Contents.json
  19. BIN
      AIRingtone/Assets.xcassets/Launch/launch_rightArrow.imageset/rightArrow@2x.png
  20. BIN
      AIRingtone/Assets.xcassets/Launch/launch_rightArrow.imageset/rightArrow@3x.png
  21. 17 0
      AIRingtone/Business/LaunchVC/TSBootPageVC.swift
  22. 40 0
      AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/Model/TSRingModel.swift
  23. 65 20
      AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/TSAIRintoneVC.swift
  24. 60 0
      AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/View/TSAIRintoneHistoryCell.swift
  25. 55 19
      AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/ViewModel/TSAIRintoneVM.swift
  26. 87 0
      AIRingtone/Business/TSCollectionViewVM/TSCollectionViewVM+Config.swift
  27. 8 1
      AIRingtone/Business/TSCollectionViewVM/TSCollectionViewVM.swift
  28. 101 0
      AIRingtone/Business/TSDiscoverVC/TSDiscoverListVC/TSDiscoverListVC.swift
  29. 130 0
      AIRingtone/Business/TSDiscoverVC/TSDiscoverListVC/VM/TSDiscoverListVM.swift
  30. 147 0
      AIRingtone/Business/TSDiscoverVC/TSDiscoverListVC/View/TSDiscoverListCell.swift
  31. 0 12
      AIRingtone/Business/TSDiscoverVC/TSDiscoverVC.swift
  32. 98 0
      AIRingtone/Business/TSDiscoverVC/TSDiscoverVC/TSDiscoverVC.swift
  33. 53 0
      AIRingtone/Business/TSDiscoverVC/TSDiscoverVC/View/TSDiscoverCell.swift
  34. 112 0
      AIRingtone/Business/TSDiscoverVC/TSDiscoverVC/ViewModel/TSDiscoverVM.swift
  35. 36 0
      AIRingtone/Business/TSDiscoverVC/TSRingDownVC/TSRingDownVC.swift
  36. 66 0
      AIRingtone/Business/TSDiscoverVC/TSRingDownVC/View/TSRingDownNullView.swift
  37. 9 0
      AIRingtone/Business/TSTabBarController/TSTabBarController.swift
  38. 0 12
      AIRingtone/Business/TSThemeVC/TSThemeVC/TSThemeVC.swift
  39. 0 1
      AIRingtone/Business/TSThemeVC/TSThemeVC/VM/TSThemeVM.swift
  40. 16 15
      AIRingtone/Business/VIewTool/TSButton.swift
  41. 6 0
      AIRingtone/Common/NetworkManager/TSNetWork/TSNetWork+Business.swift
  42. 3 1
      AIRingtone/Common/NetworkManager/TSNetWork/TSNetworkManager.swift
  43. 1 1
      AIRingtone/Common/Tool/TSBandRingTool/TSBandRingTool.swift

+ 103 - 1
AIRingtone.xcodeproj/project.pbxproj

@@ -105,6 +105,13 @@
 		A899D36F2D83C3DC00AB9C1C /* TSTutorialPopupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D36E2D83C3D500AB9C1C /* TSTutorialPopupVC.swift */; };
 		A899D3742D87B15F00AB9C1C /* TSPurchaseTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3732D87B15D00AB9C1C /* TSPurchaseTool.swift */; };
 		A899D3842D88303E00AB9C1C /* TSDiscoverVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3832D88303D00AB9C1C /* TSDiscoverVC.swift */; };
+		A899D3872D89024400AB9C1C /* TSRingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3862D89023A00AB9C1C /* TSRingModel.swift */; };
+		A899D38A2D891ADE00AB9C1C /* TSDiscoverVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3892D891ADB00AB9C1C /* TSDiscoverVM.swift */; };
+		A899D38D2D891C5600AB9C1C /* TSDiscoverCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D38C2D891C4D00AB9C1C /* TSDiscoverCell.swift */; };
+		A899D3932D8924A300AB9C1C /* TSDiscoverListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3922D8924A200AB9C1C /* TSDiscoverListVC.swift */; };
+		A899D3962D89258B00AB9C1C /* TSDiscoverListVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3952D89258600AB9C1C /* TSDiscoverListVM.swift */; };
+		A899D3992D8947D800AB9C1C /* TSDiscoverListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D3982D8947D200AB9C1C /* TSDiscoverListCell.swift */; };
+		A899D39C2D894F9800AB9C1C /* TSRingDownVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A899D39B2D894F9600AB9C1C /* TSRingDownVC.swift */; };
 		A8C6436C2D79A8C8001068D0 /* TSAIRintoneHistoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8C6436B2D79A8C7001068D0 /* TSAIRintoneHistoryCell.swift */; };
 		A8CC55822D797720002E0CAA /* TSGeneralPicBrowseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8CC55812D79771F002E0CAA /* TSGeneralPicBrowseVC.swift */; };
 		A8CC55862D798E2D002E0CAA /* TSTextGeneralRintoneVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8CC55852D798E2D002E0CAA /* TSTextGeneralRintoneVC.swift */; };
@@ -221,6 +228,14 @@
 		A899D36E2D83C3D500AB9C1C /* TSTutorialPopupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTutorialPopupVC.swift; sourceTree = "<group>"; };
 		A899D3732D87B15D00AB9C1C /* TSPurchaseTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPurchaseTool.swift; sourceTree = "<group>"; };
 		A899D3832D88303D00AB9C1C /* TSDiscoverVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiscoverVC.swift; sourceTree = "<group>"; };
+		A899D3862D89023A00AB9C1C /* TSRingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRingModel.swift; sourceTree = "<group>"; };
+		A899D3892D891ADB00AB9C1C /* TSDiscoverVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiscoverVM.swift; sourceTree = "<group>"; };
+		A899D38C2D891C4D00AB9C1C /* TSDiscoverCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiscoverCell.swift; sourceTree = "<group>"; };
+		A899D3922D8924A200AB9C1C /* TSDiscoverListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiscoverListVC.swift; sourceTree = "<group>"; };
+		A899D3952D89258600AB9C1C /* TSDiscoverListVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiscoverListVM.swift; sourceTree = "<group>"; };
+		A899D3982D8947D200AB9C1C /* TSDiscoverListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiscoverListCell.swift; sourceTree = "<group>"; };
+		A899D39B2D894F9600AB9C1C /* TSRingDownVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRingDownVC.swift; sourceTree = "<group>"; };
+		A899D39F2D895F3C00AB9C1C /* TSRingDownNullView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRingDownNullView.swift; sourceTree = "<group>"; };
 		A8C6436B2D79A8C7001068D0 /* TSAIRintoneHistoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIRintoneHistoryCell.swift; sourceTree = "<group>"; };
 		A8CC55812D79771F002E0CAA /* TSGeneralPicBrowseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGeneralPicBrowseVC.swift; sourceTree = "<group>"; };
 		A8CC55852D798E2D002E0CAA /* TSTextGeneralRintoneVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextGeneralRintoneVC.swift; sourceTree = "<group>"; };
@@ -317,8 +332,8 @@
 		A80EDE632D718B19003CD332 /* Business */ = {
 			isa = PBXGroup;
 			children = (
-				A899D3822D88303300AB9C1C /* TSDiscoverVC */,
 				A83F871B2D79408300D29B1B /* Data */,
+				A899D3822D88303300AB9C1C /* TSDiscoverVC */,
 				A899D34C2D82C61C00AB9C1C /* TSPurchaseMembershipVC */,
 				A868A8A12D7560B900F6D884 /* VIewTool */,
 				A80EDF192D71AC2F003CD332 /* TSCollectionViewVM */,
@@ -811,11 +826,90 @@
 		A899D3822D88303300AB9C1C /* TSDiscoverVC */ = {
 			isa = PBXGroup;
 			children = (
+				A899D39A2D894F8600AB9C1C /* TSRingDownVC */,
+				A899D3912D89249600AB9C1C /* TSDiscoverListVC */,
+				A899D38E2D89248500AB9C1C /* TSDiscoverVC */,
+			);
+			path = TSDiscoverVC;
+			sourceTree = "<group>";
+		};
+		A899D3852D89021400AB9C1C /* Model */ = {
+			isa = PBXGroup;
+			children = (
+				A899D3862D89023A00AB9C1C /* TSRingModel.swift */,
+			);
+			path = Model;
+			sourceTree = "<group>";
+		};
+		A899D3882D891AD100AB9C1C /* ViewModel */ = {
+			isa = PBXGroup;
+			children = (
+				A899D3892D891ADB00AB9C1C /* TSDiscoverVM.swift */,
+			);
+			path = ViewModel;
+			sourceTree = "<group>";
+		};
+		A899D38B2D891C4700AB9C1C /* View */ = {
+			isa = PBXGroup;
+			children = (
+				A899D38C2D891C4D00AB9C1C /* TSDiscoverCell.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
+		A899D38E2D89248500AB9C1C /* TSDiscoverVC */ = {
+			isa = PBXGroup;
+			children = (
+				A899D38B2D891C4700AB9C1C /* View */,
+				A899D3882D891AD100AB9C1C /* ViewModel */,
 				A899D3832D88303D00AB9C1C /* TSDiscoverVC.swift */,
 			);
 			path = TSDiscoverVC;
 			sourceTree = "<group>";
 		};
+		A899D3912D89249600AB9C1C /* TSDiscoverListVC */ = {
+			isa = PBXGroup;
+			children = (
+				A899D3972D8947C700AB9C1C /* View */,
+				A899D3942D89258000AB9C1C /* VM */,
+				A899D3922D8924A200AB9C1C /* TSDiscoverListVC.swift */,
+			);
+			path = TSDiscoverListVC;
+			sourceTree = "<group>";
+		};
+		A899D3942D89258000AB9C1C /* VM */ = {
+			isa = PBXGroup;
+			children = (
+				A899D3952D89258600AB9C1C /* TSDiscoverListVM.swift */,
+			);
+			path = VM;
+			sourceTree = "<group>";
+		};
+		A899D3972D8947C700AB9C1C /* View */ = {
+			isa = PBXGroup;
+			children = (
+				A899D3982D8947D200AB9C1C /* TSDiscoverListCell.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
+		A899D39A2D894F8600AB9C1C /* TSRingDownVC */ = {
+			isa = PBXGroup;
+			children = (
+				A899D39D2D895F3000AB9C1C /* View */,
+				A899D39B2D894F9600AB9C1C /* TSRingDownVC.swift */,
+			);
+			path = TSRingDownVC;
+			sourceTree = "<group>";
+		};
+		A899D39D2D895F3000AB9C1C /* View */ = {
+			isa = PBXGroup;
+			children = (
+				A899D39F2D895F3C00AB9C1C /* TSRingDownNullView.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
 		A8C6436A2D79A8BD001068D0 /* View */ = {
 			isa = PBXGroup;
 			children = (
@@ -844,6 +938,7 @@
 		A8CC55842D798E26002E0CAA /* TSAIRintoneVC */ = {
 			isa = PBXGroup;
 			children = (
+				A899D3852D89021400AB9C1C /* Model */,
 				A8C6436A2D79A8BD001068D0 /* View */,
 				A8CC558E2D799F93002E0CAA /* ViewModel */,
 				A80EDF102D718ECF003CD332 /* TSAIRintoneVC.swift */,
@@ -1009,6 +1104,7 @@
 				A868A8C92D76A45900F6D884 /* TSThemeSetItemView.swift in Sources */,
 				A868A9002D77E55800F6D884 /* TSPromptStyleView.swift in Sources */,
 				A83F87202D794FF000D29B1B /* TSAIPhotoImageCell.swift in Sources */,
+				A899D3932D8924A300AB9C1C /* TSDiscoverListVC.swift in Sources */,
 				A868A8DB2D76F00C00F6D884 /* TSBandRingTool.swift in Sources */,
 				A899D3602D82C8D800AB9C1C /* TSPurchaseManager.swift in Sources */,
 				A868A8F12D77081C00F6D884 /* TSContactsTool.swift in Sources */,
@@ -1018,10 +1114,12 @@
 				A80EDE652D718CAF003CD332 /* GlobalImports.swift in Sources */,
 				A868A9092D7828EC00F6D884 /* TSTGPPhotoStyleView.swift in Sources */,
 				A8DEC2702D7C395C002EB948 /* TSThemeCopyrightVC.swift in Sources */,
+				A899D3992D8947D800AB9C1C /* TSDiscoverListCell.swift in Sources */,
 				A80EDF112D718ED1003CD332 /* TSAIRintoneVC.swift in Sources */,
 				A80EDF1B2D71AC90003CD332 /* TSCollectionViewVM.swift in Sources */,
 				A80EDE612D718AF4003CD332 /* TSConfig.swift in Sources */,
 				A80EDF152D718F13003CD332 /* TSThemeVC.swift in Sources */,
+				A899D38D2D891C5600AB9C1C /* TSDiscoverCell.swift in Sources */,
 				A868A9032D77E5A500F6D884 /* TSPTPStyleModel.swift in Sources */,
 				A80EDEC62D718CEA003CD332 /* TSRandomTextPicker.swift in Sources */,
 				A80EDEC82D718CEA003CD332 /* TSNetworkManager+Loading.swift in Sources */,
@@ -1037,11 +1135,15 @@
 				A80EDEE32D718CEA003CD332 /* TSNetworkManager.swift in Sources */,
 				A868A8AF2D758BBC00F6D884 /* TSTBDesktopPreviewView.swift in Sources */,
 				A80EDEF52D718DEA003CD332 /* TSTabBarController.swift in Sources */,
+				A899D3872D89024400AB9C1C /* TSRingModel.swift in Sources */,
 				A868A8B52D7598C000F6D884 /* TSTSIslandView.swift in Sources */,
+				A899D39C2D894F9800AB9C1C /* TSRingDownVC.swift in Sources */,
 				A8272E9F2D7A8F6500F1C814 /* TSGeneralRintoneVM.swift in Sources */,
 				A868A9142D784D4D00F6D884 /* TSGeneralPicModel.swift in Sources */,
+				A899D3962D89258B00AB9C1C /* TSDiscoverListVM.swift in Sources */,
 				A868A8D52D76E41800F6D884 /* TSSetContactAvatar.swift in Sources */,
 				A868A8F52D77179D00F6D884 /* TSAIPhotoChildVC.swift in Sources */,
+				A899D38A2D891ADE00AB9C1C /* TSDiscoverVM.swift in Sources */,
 				A868A8E82D76FA4200F6D884 /* ExtAudioConverter.m in Sources */,
 				A80EDF132D718EF7003CD332 /* TSAIPhotoVC.swift in Sources */,
 				A83F87242D7954BB00D29B1B /* TSAIPhotoChildVM.swift in Sources */,

+ 22 - 0
AIRingtone/Assets.xcassets/Common/gary_right_arrow.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "gary_right_arrow@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "gary_right_arrow@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIRingtone/Assets.xcassets/Common/gary_right_arrow.imageset/gary_right_arrow@2x.png


BIN
AIRingtone/Assets.xcassets/Common/gary_right_arrow.imageset/gary_right_arrow@3x.png


BIN
AIRingtone/Assets.xcassets/Common/navi_back_white.imageset/navi_back_white@2x.png


BIN
AIRingtone/Assets.xcassets/Common/navi_back_white.imageset/navi_back_white@3x.png


+ 22 - 0
AIRingtone/Assets.xcassets/Common/ring_folder.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "ring_folder@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "ring_folder@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIRingtone/Assets.xcassets/Common/ring_folder.imageset/ring_folder@2x.png


BIN
AIRingtone/Assets.xcassets/Common/ring_folder.imageset/ring_folder@3x.png


+ 22 - 0
AIRingtone/Assets.xcassets/Common/ring_null.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "ring_null@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "ring_null@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIRingtone/Assets.xcassets/Common/ring_null.imageset/ring_null@2x.png


BIN
AIRingtone/Assets.xcassets/Common/ring_null.imageset/ring_null@3x.png


+ 6 - 0
AIRingtone/Assets.xcassets/Discover/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 22 - 0
AIRingtone/Assets.xcassets/Discover/nav_title_discover.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "nav_title_discover@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "nav_title_discover@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIRingtone/Assets.xcassets/Discover/nav_title_discover.imageset/nav_title_discover@2x.png


BIN
AIRingtone/Assets.xcassets/Discover/nav_title_discover.imageset/nav_title_discover@3x.png


+ 6 - 0
AIRingtone/Assets.xcassets/Launch/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 22 - 0
AIRingtone/Assets.xcassets/Launch/launch_rightArrow.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "rightArrow@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "rightArrow@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIRingtone/Assets.xcassets/Launch/launch_rightArrow.imageset/rightArrow@2x.png


BIN
AIRingtone/Assets.xcassets/Launch/launch_rightArrow.imageset/rightArrow@3x.png


+ 17 - 0
AIRingtone/Business/LaunchVC/TSBootPageVC.swift

@@ -18,6 +18,23 @@ class TSBootPageVC: TSBaseVC {
         fatalError("init(coder:) has not been implemented")
     }
     
+    
+    lazy var continueBtn: UIButton = {
+        let continueBtn = kCreateNormalSubmitBtn(title: "", frame: CGRectMake(0, 0, 343, 50)) {
+            
+        }
+        
+        let imageView = UIImageView.createImageView(imageName: "launch_rightArrow")
+        continueBtn.addSubview(imageView)
+        imageView.snp.makeConstraints { make in
+            make.width.height.equalTo(24)
+            make.centerY.equalToSuperview()
+            make.trailing.equalTo(-24)
+        }
+        
+        return continueBtn
+    }()
+    
     lazy var scrollView: UIScrollView = {
         let scrollView = UIScrollView()
         scrollView.isScrollEnabled = false

+ 40 - 0
AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/Model/TSRingModel.swift

@@ -0,0 +1,40 @@
+//
+//  TSRingModel.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/17.
+//
+import ObjectMapper
+
+class TSRingModel:TSBaseModel {
+    
+    var vip:Bool = false
+    var size:Int = 0
+    var title:String = ""
+    var audioUrl:String = ""
+    var duration:Int = 0
+    var categoryId:String = ""
+    
+    override func mapping(map: Map) {
+        vip <- map["vip"]
+        size <- map["size"]
+        title <- map["title"]
+        audioUrl <- map["audioUrl"]
+        duration <- map["duration"]
+        categoryId <- map["categoryId"]
+    }
+    
+}
+
+class TSRingCategoryModel:TSBaseModel {
+    
+    var cover:String = ""
+    var title:String = ""
+    var categoryId:String = ""
+    
+    override func mapping(map: Map) {
+        cover <- map["cover"]
+        title <- map["title"]
+        categoryId <- map["categoryId"]
+    }
+}

+ 65 - 20
AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/TSAIRintoneVC.swift

@@ -39,6 +39,7 @@ class TSAIRintoneVC: TSBaseVC {
         layout.minimumInteritemSpacing = 10.0
         layout.minimumLineSpacing = 18.0
         layout.headerReferenceSize = CGSizeMake(k_ScreenWidth, 48)
+        layout.footerReferenceSize = CGSizeMake(k_ScreenWidth, 48)
         return layout
     }()
     
@@ -51,6 +52,7 @@ class TSAIRintoneVC: TSBaseVC {
         collectionView.backgroundColor = .clear
         collectionView.register(TSAIRintoneHistoryCell.self, forCellWithReuseIdentifier: TSAIRintoneHistoryCell.cellID)
         collectionView.register(TSAIRintoneHistorySectionHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: TSAIRintoneHistorySectionHeaderView.reuseIdentifier)
+        collectionView.register(TSAIRintoneHistorySectionFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: TSAIRintoneHistorySectionFooterView.reuseIdentifier)
         if #available(iOS 11.0, *) {
             collectionView.contentInsetAdjustmentBehavior = .never
         }
@@ -107,6 +109,17 @@ class TSAIRintoneVC: TSBaseVC {
     
     override func dealThings() {
         updateListView()
+        
+//        //先拿下缓存的
+//        viewModel.getRingRecommend(useCache: true) { [weak self]  in guard
+//            let self = self else { return }
+//            collectionView.reloadData()
+//            //再拿下最新的数据
+            viewModel.getRingRecommend(useCache: false) { [weak self]  in guard
+                let self = self else { return }
+                collectionView.reloadData()
+            }
+//        }
     }
     
     func updateListView(){
@@ -116,14 +129,14 @@ class TSAIRintoneVC: TSBaseVC {
 
 }
 
-extension TSAIRintoneVC: UICollectionViewDataSource ,UICollectionViewDelegate {
+extension TSAIRintoneVC: UICollectionViewDataSource ,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout {
     
     public func numberOfSections(in collectionView: UICollectionView) -> Int {
-        return viewModel.historyModelList.count
+        return viewModel.modelList.count
     }
     
     public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
-        if let sectionModel = viewModel.historyModelList.safeObj(At: section) {
+        if let sectionModel = viewModel.modelList.safeObj(At: section) {
             return sectionModel.list.count
         }
         return 0
@@ -133,12 +146,18 @@ extension TSAIRintoneVC: UICollectionViewDataSource ,UICollectionViewDelegate {
         
         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TSAIRintoneHistoryCell.cellID, for: indexPath)
         
-        if let sectionModel = viewModel.historyModelList.safeObj(At: indexPath.section),
+        if let sectionModel = viewModel.modelList.safeObj(At: indexPath.section),
             let itemModel = sectionModel.list.safeObj(At: indexPath.item),
            let cell = cell as? TSAIRintoneHistoryCell
         {
             cell.delegate = self
-            cell.model = itemModel
+            
+            if let model = itemModel as? TSActionInfoModel {
+                cell.model = model
+            }else if let ringModel = itemModel as? TSRingModel {
+                cell.ringModel = ringModel
+            }
+            
             cell.setRingBtn.indexPath = indexPath
             cell.setRingBtn.addTarget(self, action: #selector(clickSetRingBtn(_ :)), for: .touchUpInside)
         }
@@ -148,10 +167,12 @@ extension TSAIRintoneVC: UICollectionViewDataSource ,UICollectionViewDelegate {
 
     @objc func clickSetRingBtn(_ btn:TSUIExpandedTouchButton){
         let indexPath = btn.indexPath
-        if let sectionModel = self.viewModel.historyModelList.safeObj(At: indexPath.section),
+        if let sectionModel = self.viewModel.modelList.safeObj(At: indexPath.section),
         let model = sectionModel.list.safeObj(At: indexPath.item){
-            ringTool.shareBand(with: model.response.musicUrl, fileName: model.response.title) { success in
-                
+            if let model = model as? TSActionInfoModel {
+                ringTool.shareBand(with: model.response.musicUrl, fileName: model.response.title)
+            }else if let ringModel = model as? TSRingModel {
+                ringTool.shareBand(with: ringModel.audioUrl, fileName: ringModel.title)
             }
         }
         
@@ -164,43 +185,67 @@ extension TSAIRintoneVC: UICollectionViewDataSource ,UICollectionViewDelegate {
     
     
     public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
-        if let sectionModel = viewModel.historyModelList.safeObj(At: indexPath.section) {
+        if let sectionModel = viewModel.modelList.safeObj(At: indexPath.section) {
             if kind == UICollectionView.elementKindSectionHeader {
                 if let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: TSAIRintoneHistorySectionHeaderView.reuseIdentifier, for: indexPath) as? TSAIRintoneHistorySectionHeaderView {
                     header.titleView.titleLab.text = sectionModel.title
                     return header
                 }
+            }else if kind == UICollectionView.elementKindSectionFooter{//},indexPath.section == (viewModel.modelList.count-1) {
+                if let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: TSAIRintoneHistorySectionFooterView.reuseIdentifier, for: indexPath) as? TSAIRintoneHistorySectionFooterView {
+                    footer.clickView = {
+                        TSTabBarController.selectedIndex(1)
+                    }
+                    return footer
+                }
             }
         }
-        return TSAIRintoneHistorySectionHeaderView()
+        return UICollectionReusableView()
+    }
+    
+    
+    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
+        if section == viewModel.modelList.count - 1 {
+            return  CGSizeMake(k_ScreenWidth, 48)
+        }
+        return .zero
     }
+    
+
 }
 
 extension TSAIRintoneVC: SwipeCollectionViewCellDelegate {
     
     func collectionView(_ collectionView: UICollectionView, editActionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
         guard orientation == .right else { return nil }
-
+        
+        if indexPath.section != 0 {
+            return nil
+        }
+        
         // 删除操作
         let deleteAction = SwipeAction(style: .destructive, title: nil) {[weak self] action, indexPath in
             guard let self = self else { return }
             showCustomAlert(message: "Are you sure to delete".localized, deleteHandler:  {
-                if let sectionModel = self.viewModel.historyModelList.safeObj(At: indexPath.section),
+                if let sectionModel = self.viewModel.modelList.safeObj(At: indexPath.section),
                 let model = sectionModel.list.safeObj(At: indexPath.item){
-                    collectionView.performBatchUpdates({
-                        self.viewModel.removeModel(model: model)
-                        if sectionModel.list.count == 0 {
-                            collectionView.deleteSections([indexPath.section])
-                        }else{
-                            collectionView.deleteItems(at: [indexPath])
-                        }
-                    })
+                    if let model = model as? TSActionInfoModel {
+                        collectionView.performBatchUpdates({
+                            self.viewModel.removeModel(model: model)
+                            if sectionModel.list.count == 0 {
+                                collectionView.deleteSections([indexPath.section])
+                            }else{
+                                collectionView.deleteItems(at: [indexPath])
+                            }
+                        })
+                    }
                 }
             })
         }
         
         deleteAction.backgroundColor = "#E83E3E".uiColor
         deleteAction.image = UIImage(named: "delete_white")
+        
         return [deleteAction]
     }
     

+ 60 - 0
AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/View/TSAIRintoneHistoryCell.swift

@@ -64,6 +64,16 @@ class TSAIRintoneHistoryCell: SwipeCollectionViewCell {
         }
     }
     
+    var ringModel:TSRingModel?{
+        didSet{
+            guard let ringModel = ringModel else { return }
+            ringView.timeLab.text = Float(ringModel.duration).floatToMinuteSecond()
+            ringView.nameLab.text = ringModel.title
+            ringView.setCoverImageView(urlString: "")
+            exampleView.isHidden = true
+        }
+    }
+    
     override var isSelected: Bool{
         didSet{
             ringView.isloading = isSelected
@@ -162,3 +172,53 @@ class TSAIRintoneHistorySectionHeaderView: UICollectionReusableView {
         fatalError("init(coder:) has not been implemented")
     }
 }
+
+class TSAIRintoneHistorySectionFooterView: UICollectionReusableView {
+    static let reuseIdentifier = "TSAIRintoneHistorySectionFooterView"
+    var clickView:(()->Void)?
+    
+    lazy var moreView: UIView = {
+        let moreView = UIView()
+        moreView.backgroundColor = .white.withAlphaComponent(0.1)
+        moreView.cornerRadius = 10.0
+        let textLabel = UILabel.createLabel(text: "more".localized,font: .font(size: 12),textColor: .white.withAlphaComponent(0.6))
+        moreView.addSubview(textLabel)
+        
+        let imageView = UIImageView.createImageView(imageName: "gary_right_arrow")
+        moreView.addSubview(imageView)
+        
+        textLabel.snp.makeConstraints { make in
+            make.leading.equalTo(8)
+            make.centerY.equalToSuperview()
+        }
+        
+        imageView.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.leading.equalTo(textLabel.snp.trailing).offset(2)
+            make.width.height.equalTo(12)
+            make.trailing.equalTo(-4)
+        }
+        moreView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(clickMoreView)))
+        return moreView
+    }()
+    
+    
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        
+        addSubview(moreView)
+        moreView.snp.makeConstraints { make in
+            make.center.equalToSuperview()
+            make.height.equalTo(20)
+        }
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    @objc func clickMoreView() {
+        clickView?()
+    }
+}

+ 55 - 19
AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/ViewModel/TSAIRintoneVM.swift

@@ -4,42 +4,78 @@
 //
 //  Created by 100Years on 2025/3/6.
 //
+import ObjectMapper
+class TSAIRintoneHistoryModel {
+    var title:String = ""
+    var list:[Any] = []
+    
+    init(title: String, list: [Any]) {
+        self.title = title
+        self.list = list
+    }
+}
 
 class TSAIRintoneVM {
-    lazy var aiRintoneHistoryModel: TSAIRintoneHistoryModel = {
-        let model = TSAIRintoneHistoryModel(title: "Generate History", list:TSAIRintoneHistory.listModelArray)
-        return model
-    }()
     
-    lazy var historyModelList: [TSAIRintoneHistoryModel] = {
-        gethistoryModelList()
+    lazy var modelList: [TSAIRintoneHistoryModel] = {
+        getModelList()
     }()
     
-    private func gethistoryModelList()->[TSAIRintoneHistoryModel]{
+    private func getModelList()->[TSAIRintoneHistoryModel]{
+        var list = [TSAIRintoneHistoryModel]()
+        
         if aiRintoneHistoryModel.list.count > 0 {
-            return [aiRintoneHistoryModel]
+            list.append(aiRintoneHistoryModel)
         }
-        return []
+        
+        if recommendRingtonesModel.list.count > 0 {
+            list.append(recommendRingtonesModel)
+        }
+        
+        return list
     }
     
+    //生成的历史
+    lazy var aiRintoneHistoryModel: TSAIRintoneHistoryModel = {
+        let model = TSAIRintoneHistoryModel(title: "Generate History", list:TSAIRintoneHistory.listModelArray)
+        return model
+    }()
+    
+
+    //每日推荐的歌曲
+    lazy var recommendRingtonesModel: TSAIRintoneHistoryModel = {
+        let model = TSAIRintoneHistoryModel(title: "Recommend Ringtones", list:[])
+        return model
+    }()
+
+}
+
+
+extension TSAIRintoneVM {
+    
     func updateRecentData() {
-        aiRintoneHistoryModel.list = TSAIRintoneHistory.listModelArray
-        historyModelList = gethistoryModelList()
+        aiRintoneHistoryModel.list = Array(TSAIRintoneHistory.listModelArray.prefix(2))
+        modelList = getModelList()
     }
     
     func removeModel(model:TSActionInfoModel){
         TSAIRintoneHistory.removeModel(model: model)
         updateRecentData()
     }
+    
 }
 
-
-class TSAIRintoneHistoryModel {
-    var title:String = ""
-    var list:[TSActionInfoModel] = []
-    
-    init(title: String, list: [TSActionInfoModel]) {
-        self.title = title
-        self.list = list
+extension TSAIRintoneVM {
+    func getRingRecommend(useCache:Bool,complete:@escaping ()->Void){
+        _ = TSNetworkShared.get(urlType: .ringRecommend,useCache: useCache) { [weak self] data,error in
+            guard let self = self else { return }
+            if let result = kNetWorkResultArraySuccess(data: data) {
+                if let list =  Mapper<TSRingModel>().mapArray(JSONObject: result) {
+                    recommendRingtonesModel.list = list
+                    updateRecentData()
+                }
+            }
+            complete()
+        }
     }
 }

+ 87 - 0
AIRingtone/Business/TSCollectionViewVM/TSCollectionViewVM+Config.swift

@@ -201,3 +201,90 @@ let photoHistoryConfig:TSColVVMSizeConfig = {
     
     return config
 }()
+
+//MARK: ring Categories 分类配置
+let ringCategoriesConfig:TSColVVMSizeConfig = {
+
+    let sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
+    let lineSpacing = 16.0
+    let itemSpacing = 16.0
+    
+    let originalSize = CGSizeMake(104.0, 104.0)
+    
+    let cellRowNum = 3
+    
+    let originalScale = originalSize.width/originalSize.height
+    var w = k_ScreenWidth-sectionInset.left-sectionInset.right
+    w = w - lineSpacing * CGFloat((cellRowNum-1))
+    w = w/CGFloat(cellRowNum)
+    let h = w/originalScale
+    
+    let cellSize = CGSizeMake(w, h)
+    let cellClass = TSDiscoverCell.self
+    
+    let headerViewSize = CGSizeMake(k_ScreenWidth, 0)
+    let headerView = TSColVVMSectionHeaderView.self
+
+    let footerViewSize = CGSizeMake(k_ScreenWidth, 0)
+    let footerView = UICollectionReusableView.self
+    
+    var config = TSColVVMSizeConfig(
+        sectionInset: sectionInset,
+        lineSpacing: lineSpacing,
+        itemSpacing: itemSpacing,
+        originalSize: originalSize,
+        cellRowNum: cellRowNum,
+        cellSize: cellSize,
+        cellClass: cellClass,
+        headerViewSize: headerViewSize,
+        headerView: headerView,
+        footerViewSize: footerViewSize,
+        footerView: footerView
+    )
+    
+    return config
+}()
+
+
+//MARK: ring list 分类配置
+let ringListConfig:TSColVVMSizeConfig = {
+
+    let sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
+    let lineSpacing = 13.0
+    let itemSpacing = 13.0
+    
+    let originalSize = CGSizeMake(343.0, 72)
+    
+    let cellRowNum = 1
+    
+    let originalScale = originalSize.width/originalSize.height
+    var w = k_ScreenWidth-sectionInset.left-sectionInset.right
+    w = w - lineSpacing * CGFloat((cellRowNum-1))
+    w = w/CGFloat(cellRowNum)
+    let h = w/originalScale
+    
+    let cellSize = CGSizeMake(w, h)
+    let cellClass = TSDiscoverListCell.self
+    
+    let headerViewSize = CGSizeMake(k_ScreenWidth, 0)
+    let headerView = TSColVVMSectionHeaderView.self
+
+    let footerViewSize = CGSizeMake(k_ScreenWidth, 0)
+    let footerView = UICollectionReusableView.self
+    
+    var config = TSColVVMSizeConfig(
+        sectionInset: sectionInset,
+        lineSpacing: lineSpacing,
+        itemSpacing: itemSpacing,
+        originalSize: originalSize,
+        cellRowNum: cellRowNum,
+        cellSize: cellSize,
+        cellClass: cellClass,
+        headerViewSize: headerViewSize,
+        headerView: headerView,
+        footerViewSize: footerViewSize,
+        footerView: footerView
+    )
+    
+    return config
+}()

+ 8 - 1
AIRingtone/Business/TSCollectionViewVM/TSCollectionViewVM.swift

@@ -15,7 +15,10 @@ enum TSColVVMStyple : Int {
     
     case posterHistory   //生成海报的历史记录
     case photoHistory    //生成头像的历史记录
-
+    
+    case ringCategories    //铃声分类
+    case ringList       //铃声列表
+    
     var config:TSColVVMSizeConfig {
         switch self {
         case .themeGuide:
@@ -26,6 +29,10 @@ enum TSColVVMStyple : Int {
             return posterHistoryConfig
         case .photoHistory:
             return photoHistoryConfig
+        case .ringCategories:
+            return ringCategoriesConfig
+        case .ringList:
+            return ringListConfig
         }
     }
     

+ 101 - 0
AIRingtone/Business/TSDiscoverVC/TSDiscoverListVC/TSDiscoverListVC.swift

@@ -0,0 +1,101 @@
+//
+//  TSDiscoverListVC.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/17.
+//
+
+class TSDiscoverListVC: TSBaseVC {
+    
+    var ringCategoryModel:TSRingCategoryModel
+    init(ringCategoryModel: TSRingCategoryModel) {
+        self.ringCategoryModel = ringCategoryModel
+        super.init()
+    }
+    
+    @MainActor required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    lazy var viewModel:TSDiscoverListVM = {
+        let viewModel = TSDiscoverListVM(categoryID:ringCategoryModel.categoryId,animationView: self.view)
+        return viewModel
+    }()
+    
+
+    lazy var collectionComponent: TSCollectionViewComponent = {
+        let layout = UICollectionViewFlowLayout()
+        let cp = TSCollectionViewComponent(frame: CGRect.zero, layout: layout, attributes: [ :])
+        cp.collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0)
+        
+//        cp.itemDidSelectedHandler = { [weak self] (object, indexPath) in
+//            guard let self = self else { return }
+//            if indexPath.section == 0{
+//                kPushVC(target: self, modelVC: TSAIPhotoVC())
+//                return
+//            }
+//
+//            if let sections = viewModel.colDataArray.safeObj(At: indexPath.section) as? TSColVVMSectionModel{
+//                let browseVC = TSThemeBrowseVC(currentIndex: indexPath.row, themeViewModel: viewModel) { [weak self]  in
+//                    guard let self = self else { return }
+//                    collectionComponent.reloadData()
+//                }
+//                kPushVC(target: self, modelVC: browseVC)
+//            }
+//        }
+        
+        cp.collectionView.addRefresh{ [weak self]  in
+            guard let self = self else { return }
+            viewModel.reloadAllData { [weak self] success  in
+                guard let self = self else { return }
+                if success {
+                    collectionComponent.clear()
+                    collectionComponent.reloadView(with:viewModel.colDataArray)
+                }
+                collectionComponent.collectionView.endRefreshing()
+            }
+        }
+        
+        cp.collectionView.addLoadMore { [weak self]  in
+            guard let self = self else { return }
+            
+            viewModel.getMoreData { [weak self] success,haveMore in
+                guard let self = self else { return }
+                if success {
+                    collectionComponent.reloadData()
+                }
+                collectionComponent.collectionView.endRefreshing(noMore: !haveMore)
+            }
+        }
+        return cp
+    }()
+    
+    
+    override func createView() {
+        addNormalNavBarView()
+        setPageTitle(ringCategoryModel.title)
+        _ = setNavigationItem("", imageName: "tutorials", direction: .right, action: #selector(clickRight))
+        
+        contentView.addSubview(collectionComponent.collectionView)
+        collectionComponent.collectionView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+
+    }
+    
+    override func dealThings() {
+        viewModel.getData {[weak self] success in
+            guard let self = self else { return }
+            if success {
+                collectionComponent.clear()
+                collectionComponent.reloadView(with:viewModel.colDataArray)
+            }
+        }
+    }
+    
+    @objc func clickRight(){
+        let browseVC = TSThemeTutorialsVC()
+        kPushVC(target: self, modelVC: browseVC)
+    }
+}
+

+ 130 - 0
AIRingtone/Business/TSDiscoverVC/TSDiscoverListVC/VM/TSDiscoverListVM.swift

@@ -0,0 +1,130 @@
+//
+//  TSDiscoverListVM.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/17.
+//
+
+
+import ObjectMapper
+class TSDiscoverListVM {
+
+    var colDataArray:[TSComponent] = [TSComponent]()
+    
+    lazy var contentSecitonModel: TSColVVMSectionModel = {
+        let contentSeciton = TSColVVMSectionModel()
+        contentSeciton.style = .ringList
+        return contentSeciton
+    }()
+    
+    
+    var listModelArray: [TSRingModel] = []
+    
+    init(categoryID: String,animationView:UIView) {
+        self.categoryID = categoryID
+        self.animationView = animationView
+        combinedData()
+    }
+    
+    func combinedData(){
+        colDataArray.removeAll()
+        colDataArray.append(contentSecitonModel)
+    }
+    
+    weak var animationView:UIView?
+    var pageSize:Int = kPageSize
+    var pageNum:Int = kPageNum
+    var categoryID:String
+
+}
+
+extension TSDiscoverListVM {
+    func setContentItemModels(){
+        var array = [TSColVVMItemModel]()
+        listModelArray.forEach { themeModel in
+            let itemModel = TSColVVMItemModel()
+            itemModel.dataModel = themeModel
+            itemModel.style = .ringList
+            array.append(itemModel)
+        }
+        contentSecitonModel.items = array
+    }
+}
+ 
+extension TSDiscoverListVM {
+    
+    func getData(complete:@escaping (Bool)->Void){
+        pageNum = kPageNum
+        getNetWorkData(useCache: true,animationView: animationView) { [weak self] modelArray,success  in
+            guard let self = self else { return }
+            if modelArray.count > 0 {
+                listModelArray.removeAll()
+                listModelArray.append(contentsOf: modelArray)
+                setContentItemModels()
+                complete(true)
+            }else{
+                complete(false)
+            }
+        }
+    }
+    
+    /// 上拉刷新
+    /// - Parameter complete: (是否成功)
+    func reloadAllData(complete:@escaping (Bool)->Void){
+        TSNetworkShared.removeCache(urlType:.rings)
+        pageNum = kPageNum
+        getNetWorkData(useCache: false,animationView: nil) {[weak self] modelArray,success  in
+            guard let self = self else { return }
+            if modelArray.count > 0 {
+                listModelArray.removeAll()
+                listModelArray.append(contentsOf: modelArray)
+                setContentItemModels()
+                complete(true)
+            }else{
+                complete(false)
+            }
+        }
+    }
+    
+    /// 获取更多数据
+    /// - Parameter complete: (是否成功,还有更多数据)
+    func getMoreData(complete:@escaping (Bool,Bool)->Void){
+        pageNum+=1
+        getNetWorkData(useCache: true,animationView: nil) { [weak self] modelArray,success in
+            guard let self = self else { return }
+            if modelArray.count > 0 {
+                listModelArray.append(contentsOf: modelArray)
+                setContentItemModels()
+                complete(success,true)
+            }else{
+                complete(success,false)
+            }
+        }
+    }
+
+    func getNetWorkData(useCache:Bool,animationView:UIView?,complete:@escaping ([TSRingModel],Bool)->Void){
+        
+        let parameters:[String:Any] = [
+            "page_size":pageSize,
+            "page":pageNum,
+            "category_id":categoryID,
+        ]
+        
+        _ = TSNetworkShared.get(
+            urlType: .rings,
+            parameters: parameters,
+            animationView: animationView,
+            useCache: useCache)
+        {  data, error in
+            if let result = kNetWorkResultArraySuccess(data: data) {
+                let modelArray = Mapper<TSRingModel>().mapArray(JSONArray: result)
+                complete(modelArray,true)
+            }else{
+                complete([],false)
+            }
+        }
+    }
+    
+}
+
+

+ 147 - 0
AIRingtone/Business/TSDiscoverVC/TSDiscoverListVC/View/TSDiscoverListCell.swift

@@ -0,0 +1,147 @@
+//
+//  TSDiscoverListCell.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/17.
+//
+
+class TSDiscoverListCell: TSBaseCollectionCell {
+    
+    static let cellID = "TSDiscoverListCell"
+    lazy var setRingBtn: TSUIExpandedTouchButton = {
+        let setRingBtn = TSUIExpandedTouchButton()
+        setRingBtn.setImage(UIImage(named: "ai_setRing_icon"), for: .normal)
+        return setRingBtn
+    }()
+    
+    lazy var ringView: TSRingToneCellView = {
+        let ringToneView = TSRingToneCellView()
+        ringToneView.backgroundColor = .clear
+        return ringToneView
+    }()
+    
+    
+    lazy var exampleView: UIView = {
+        let exampleView = UIView()
+        exampleView.backgroundColor = "#7E57F4".uiColor
+        
+        let textLabel = UILabel.createLabel(
+            text: "Example".localized,
+            font: .font(size: 12),
+            textColor: .white
+        )
+        
+        exampleView.addSubview(textLabel)
+        textLabel.snp.makeConstraints { make in
+            make.top.edges.equalTo(UIEdgeInsets(top: 4, left: 6, bottom: 4, right: 6))
+        }
+        
+        kDelayMainShort {
+            exampleView.makeCorner([.topLeft,.bottomLeft], radius: 10)
+        }
+//        exampleView.isHidden = true
+        return exampleView
+    }()
+    
+    var model:TSActionInfoModel?{
+        didSet{
+            guard let model = model else { return }
+            ringView.timeLab.text = Float(model.request.duration).floatToMinuteSecond()
+            ringView.nameLab.text = model.response.title
+            ringView.setCoverImageView(urlString: model.response.coverUrl)
+            exampleView.isHidden = model.modelType != .example
+        }
+    }
+    
+    var ringModel:TSRingModel?{
+        didSet{
+            guard let ringModel = ringModel else { return }
+            ringView.timeLab.text = Float(ringModel.duration).floatToMinuteSecond()
+            ringView.nameLab.text = ringModel.title
+            ringView.setCoverImageView(urlString: "")
+            exampleView.isHidden = true
+        }
+    }
+    
+    override var isSelected: Bool{
+        didSet{
+            ringView.isloading = isSelected
+            if isSelected, self.ringView.isPlay == false{
+                TSBusinessAudioPlayer.shared.playUrlString( model?.response.musicUrl)
+            }else{
+                TSBusinessAudioPlayer.shared.stop()
+                backgroundColor = .cardColor
+            }
+        }
+    }
+    
+    override func creatUI() {
+        cornerRadius = 16.0
+        backgroundColor = .cardColor
+  
+        contentView.addSubview(exampleView)
+        exampleView.snp.makeConstraints { make in
+            make.trailing.equalToSuperview()
+            make.top.equalTo(0)
+            make.height.equalTo(20)
+        }
+  
+        contentView.addSubview(ringView)
+        ringView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+    
+        contentView.addSubview(setRingBtn)
+        setRingBtn.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.trailing.equalTo(-16)
+            make.width.height.equalTo(20)
+        }
+        
+        NotificationCenter.default.addObserver(forName: .kBusinessAudioStateChange, object: nil, queue: nil) { notification in
+            if let userInfo = notification.userInfo as? [String: TSBusinessAudioPlayer.PlayerState], let state = userInfo["PlayerState"] {
+                kExecuteOnMainThread {
+                    self.changePlayerState(state: state)
+                }
+                
+            }
+        }
+    }
+    
+    func changePlayerState(state:TSBusinessAudioPlayer.PlayerState){
+        if self.isSelected == false {
+            self.ringView.isPlay = false
+            return
+        }
+        
+        switch state {
+        case .loading(let progress):
+            if progress == 0.0 {
+                self.ringView.isloading = true
+            }else if progress == 1.0 {
+                self.ringView.isloading = false
+            }
+        case .play:
+            self.ringView.isPlay = true
+            backgroundColor = "#3C213F".uiColor
+        case .stop:
+            self.ringView.isPlay = false
+            backgroundColor = .cardColor
+        default:
+            break
+        }
+    }
+
+    override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
+        super.renderView(with: object, component: component, attributes: attributes)
+        if let itemModel = object as? TSColVVMItemModel{
+            if let dataModel = itemModel.dataModel as? TSRingModel{
+                ringModel = dataModel
+            }
+        }
+    }
+    
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
+}

+ 0 - 12
AIRingtone/Business/TSDiscoverVC/TSDiscoverVC.swift

@@ -1,12 +0,0 @@
-//
-//  TSDiscoverVC.swift
-//  AIRingtone
-//
-//  Created by 100Years on 2025/3/17.
-//
-
-class TSDiscoverVC: TSBaseVC {
-    override func createView() {
-        view.backgroundColor = .green
-    }
-}

+ 98 - 0
AIRingtone/Business/TSDiscoverVC/TSDiscoverVC/TSDiscoverVC.swift

@@ -0,0 +1,98 @@
+//
+//  TSDiscoverVC.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/17.
+//
+
+class TSDiscoverVC: TSBaseVC {
+    lazy var viewModel:TSDiscoverVM = {
+        let viewModel = TSDiscoverVM()
+        viewModel.animationView = self.view
+        return viewModel
+    }()
+    
+    lazy var navBarView: TSBaseNavContentBarView = {
+        let navBarView = TSBaseNavContentBarView()
+
+        let titleImageView = UIImageView.createImageView(imageName: "nav_title_discover",contentMode: .scaleToFill)
+        navBarView.barView.addSubview(titleImageView)
+        titleImageView.snp.makeConstraints { make in
+            make.center.equalToSuperview()
+            make.width.equalTo(375)
+            make.height.equalTo(48)
+        }
+        
+        let rightBtn = UIButton.createButton(image: UIImage(named: "ring_folder")){[weak self]  in
+            guard let self = self else { return }
+            kPushVC(target: self, modelVC: TSRingDownVC())
+        }
+        navBarView.barView.addSubview(rightBtn)
+        rightBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-16)
+            make.centerY.equalToSuperview()
+            make.width.equalTo(24)
+            make.height.equalTo(24)
+        }
+        
+        return navBarView
+    }()
+    
+    
+    lazy var collectionComponent: TSCollectionViewComponent = {
+        let layout = UICollectionViewFlowLayout()
+        let cp = TSCollectionViewComponent(frame: CGRect.zero, layout: layout, attributes: [ :])
+        cp.collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0)
+        
+        cp.itemDidSelectedHandler = { [weak self] (object, indexPath) in
+            guard let self = self else { return }
+            if let sections = viewModel.colDataArray.safeObj(At: indexPath.section) as? TSColVVMSectionModel,
+               let itemModel = sections.items.safeObj(At: indexPath.row),
+                let ringCategoryModel = itemModel.dataModel as? TSRingCategoryModel{
+                kPushVC(target: self, modelVC: TSDiscoverListVC(ringCategoryModel: ringCategoryModel))
+            }
+        }
+        
+        cp.collectionView.addRefresh{ [weak self]  in
+            guard let self = self else { return }
+            viewModel.reloadAllData { [weak self] success  in
+                guard let self = self else { return }
+                if success {
+                    collectionComponent.clear()
+                    collectionComponent.reloadView(with:viewModel.colDataArray)
+                }
+                collectionComponent.collectionView.endRefreshing()
+            }
+        }
+        
+        return cp
+    }()
+    
+    
+    
+    override func createView() {
+        
+        setViewBgImageNamed(named: kViewBJ)
+
+        edgesForExtendedLayout = []
+        navBarContentView.addSubview(navBarView)
+        navBarView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        
+        contentView.addSubview(collectionComponent.collectionView)
+        collectionComponent.collectionView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+    }
+    
+    override func dealThings() {
+        viewModel.getData {[weak self] success in
+            guard let self = self else { return }
+            if success {
+                collectionComponent.clear()
+                collectionComponent.reloadView(with:viewModel.colDataArray)
+            }
+        }
+    }
+}

+ 53 - 0
AIRingtone/Business/TSDiscoverVC/TSDiscoverVC/View/TSDiscoverCell.swift

@@ -0,0 +1,53 @@
+//
+//  TSDiscoverCell.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/17.
+//
+
+class TSDiscoverCell: TSBaseCollectionCell {
+    static let cellID = "TSDiscoverCell"
+
+
+    var itemModel:TSRingCategoryModel = TSRingCategoryModel(){
+        didSet{
+            imageView.image = UIImage(named: itemModel.cover)
+            textLabel.text = itemModel.title
+        }
+    }
+    
+    lazy var imageView: UIImageView = {
+        let imageView = UIImageView()
+        return imageView
+    }()
+    
+    lazy var textLabel: UILabel = {
+        let textLabel = UILabel.createLabel(font: .font(size: 14),textColor: .white,textAlignment: .center,numberOfLines: 0)
+        return textLabel
+    }()
+    
+    override func creatUI() {
+        bgContentView.cornerRadius = 16.0
+        bgContentView.addSubview(imageView)
+        imageView.snp.makeConstraints { make in
+            make.top.bottom.leading.trailing.equalTo(0)
+        }
+        
+        bgContentView.addSubview(textLabel)
+        textLabel.snp.makeConstraints { make in
+            make.leading.equalTo(6)
+            make.trailing.equalTo(-6)
+            make.top.equalTo(9)
+        }
+    }
+    
+    override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
+        super.renderView(with: object, component: component, attributes: attributes)
+        if let itemModel = object as? TSColVVMItemModel{
+            if let dataModel = itemModel.dataModel as? TSRingCategoryModel{
+                imageView.setAsyncImage(urlString: dataModel.cover,placeholder:kPlaceholderImage,contentMode: .scaleAspectFill)
+                textLabel.text = dataModel.title
+            }
+        }
+    }
+}

+ 112 - 0
AIRingtone/Business/TSDiscoverVC/TSDiscoverVC/ViewModel/TSDiscoverVM.swift

@@ -0,0 +1,112 @@
+//
+//  TSDiscoverVM.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/17.
+//
+
+import ObjectMapper
+class TSDiscoverVM {
+
+    var colDataArray:[TSComponent] = [TSComponent]()
+    
+    lazy var contentSecitonModel: TSColVVMSectionModel = {
+        let contentSeciton = TSColVVMSectionModel()
+        contentSeciton.style = .ringCategories
+        return contentSeciton
+    }()
+    
+    
+    var listModelArray: [TSRingCategoryModel] = []
+    
+    init() {
+        combinedData()
+    }
+    
+    func combinedData(){
+        colDataArray.removeAll()
+        colDataArray.append(contentSecitonModel)
+    }
+    
+    weak var animationView:UIView?
+    var pageSize:Int = kPageSize
+    var pageNum:Int = kPageNum
+
+
+}
+
+extension TSDiscoverVM {
+    func setContentItemModels(){
+        var array = [TSColVVMItemModel]()
+        listModelArray.forEach { themeModel in
+            let itemModel = TSColVVMItemModel()
+            itemModel.dataModel = themeModel
+            itemModel.style = .ringCategories
+            array.append(itemModel)
+        }
+        contentSecitonModel.items = array
+    }
+}
+ 
+extension TSDiscoverVM {
+    
+    func getData(complete:@escaping (Bool)->Void){
+        pageNum = kPageNum
+        getNetWorkData(useCache: true,animationView: animationView) { [weak self] modelArray,success  in
+            guard let self = self else { return }
+            if modelArray.count > 0 {
+                listModelArray.removeAll()
+                listModelArray.append(contentsOf: modelArray)
+                setContentItemModels()
+                complete(true)
+            }else{
+                complete(false)
+            }
+        }
+    }
+    
+    /// 上拉刷新
+    /// - Parameter complete: (是否成功)
+    func reloadAllData(complete:@escaping (Bool)->Void){
+        TSNetworkShared.removeCache(urlType:.ringCategories)
+        pageNum = kPageNum
+        getNetWorkData(useCache: false,animationView: nil) {[weak self] modelArray,success  in
+            guard let self = self else { return }
+            if modelArray.count > 0 {
+                listModelArray.removeAll()
+                listModelArray.append(contentsOf: modelArray)
+                setContentItemModels()
+                complete(true)
+            }else{
+                complete(false)
+            }
+        }
+    }
+
+    func getNetWorkData(useCache:Bool,animationView:UIView?,complete:@escaping ([TSRingCategoryModel],Bool)->Void){
+        
+        let parameters:[String:Any] = [
+            "page_size":pageSize,
+            "page":pageNum
+        ]
+        
+        _ = TSNetworkShared.get(
+            urlType: .ringCategories,
+            parameters: parameters,
+            animationView: animationView,
+            useCache: useCache)
+        { [weak self] data, error in
+            guard let self = self else { return }
+            
+            if let result = kNetWorkResultArraySuccess(data: data) {
+                let modelArray = Mapper<TSRingCategoryModel>().mapArray(JSONArray: result)
+                complete(modelArray,true)
+            }else{
+                complete([],false)
+            }
+        }
+    }
+    
+}
+
+

+ 36 - 0
AIRingtone/Business/TSDiscoverVC/TSRingDownVC/TSRingDownVC.swift

@@ -0,0 +1,36 @@
+//
+//  TSRingDownVC.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/17.
+//
+
+class TSRingDownVC: TSBaseVC {
+    
+    
+    lazy var nullView:TSRingDownNullView = TSRingDownNullView { [weak self]  in
+        guard let self = self else { return }
+         
+        let ringCategoryModel = TSRingCategoryModel()
+        ringCategoryModel.title = "Popular"
+        ringCategoryModel.cover = "http://d3a93z8fj970a4.cloudfront.net/fbae944d-9e49-46ee-a51f-78f75a1952e8"
+        ringCategoryModel.categoryId = "Popular"
+        kPushVC(target: self, modelVC: TSDiscoverListVC(ringCategoryModel: ringCategoryModel))
+    }
+    
+    override func createView() {
+ 
+        addNormalNavBarView()
+        setPageTitle("Downloaded".localized)
+        _ = setNavigationItem("", imageName: "tutorials", direction: .right, action: #selector(clickRight))
+
+        contentView.addSubview(nullView)
+        
+    }
+    
+    @objc func clickRight(){
+        kPushVC(target: self, modelVC: TSThemeTutorialsVC())
+    }
+}
+
+

+ 66 - 0
AIRingtone/Business/TSDiscoverVC/TSRingDownVC/View/TSRingDownNullView.swift

@@ -0,0 +1,66 @@
+//
+//  TSRingDownNullView.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/18.
+//
+
+class TSRingDownNullView: TSBaseView {
+    var clickBlock:()->Void
+    
+    init(clickBlock: @escaping () -> Void) {
+        self.clickBlock = clickBlock
+        super.init(frame: .zero)
+    }
+    
+    @MainActor required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    lazy var imageView: UIImageView = {
+        return UIImageView.createImageView(imageName: "ring_null")
+    }()
+    
+    lazy var titleLabel: UILabel = {
+        let titleLabel = UILabel.createLabel(text:"No Ringtone".localized ,font:.font(size: 14.0),textColor: .white,textAlignment: .center,numberOfLines: 0)
+        return titleLabel
+    }()
+    
+    lazy var submitBtn: UIButton = {
+        let button = kCreateNormalSubmitBtn(title: "Explore".localized, frame: CGRectMake(0, 0, 200, 48),action: { [weak self]  in
+            guard let self = self else { return }
+            clickBlock()
+        })
+        return button
+    }()
+
+    override func creatUI() {
+        
+        self.frame = CGRectMake(0, 0, k_ScreenWidth, k_ScreenHeight - k_Nav_Height)
+        
+        contentView.addSubview(imageView)
+        imageView.snp.makeConstraints { make in
+            make.top.equalTo(112)
+            make.centerX.equalToSuperview()
+            make.width.height.equalTo(120)
+        }
+
+        contentView.addSubview(titleLabel)
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalTo(imageView.snp.bottom).offset(4)
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-16)
+            make.height.equalTo(20)
+        }
+        
+        contentView.addSubview(submitBtn)
+        submitBtn.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(40)
+            make.centerX.equalToSuperview()
+            make.width.equalTo(submitBtn.width)
+            make.height.equalTo(submitBtn.height)
+        }
+
+    }
+    
+}

+ 9 - 0
AIRingtone/Business/TSTabBarController/TSTabBarController.swift

@@ -87,3 +87,12 @@ class TSTabBarController: UITabBarController {
     
 
 }
+extension TSTabBarController {
+    
+    static func selectedIndex(_ selectedIndex:Int){
+        if let tabBarVC = WindowHelper.getKeyWindow()?.rootViewController as? TSTabBarController{
+            tabBarVC.selectedIndex = selectedIndex
+        }
+    }
+    
+}

+ 0 - 12
AIRingtone/Business/TSThemeVC/TSThemeVC/TSThemeVC.swift

@@ -44,16 +44,6 @@ class TSThemeVC: TSBaseVC {
         let cp = TSCollectionViewComponent(frame: CGRect.zero, layout: layout, attributes: [ :])
         cp.collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0)
         
-        cp.itemActionHandler = { [weak self] cellCp, indexPath in
-            guard let self = self else { return }
-            if let text = cellCp as? String ,indexPath == IndexPath(item: 0, section: 0) {
-            }
-        }
-        
-//        cp.sectionActionHandler = { [weak self] cellCp, indexPath in
-//            guard let self = self else { return }
-//        }
-        
         cp.itemDidSelectedHandler = { [weak self] (object, indexPath) in
             guard let self = self else { return }
             if indexPath.section == 0{
@@ -71,10 +61,8 @@ class TSThemeVC: TSBaseVC {
 
         }
         
-        
         cp.collectionView.addRefresh{ [weak self]  in
             guard let self = self else { return }
-            
             viewModel.reloadAllData { [weak self] success  in
                 guard let self = self else { return }
                 if success {

+ 0 - 1
AIRingtone/Business/TSThemeVC/TSThemeVC/VM/TSThemeVM.swift

@@ -6,7 +6,6 @@
 //
 
 import ObjectMapper
-import Combine
 class TSThemeVM {
 
     var colDataArray:[TSComponent] = [TSComponent]()

+ 16 - 15
AIRingtone/Business/VIewTool/TSButton.swift

@@ -131,18 +131,12 @@ extension TSAppBtnView{
 }
 //创造按钮
 extension TSAppBtnView{
-    //常用提交按钮
-    func kCreateNormalSubmitBtn(title:String, action: (() -> Void)? = nil) -> UIButton {
-        let btn = TSNormalSubmitBtn()
-        btn.setUpButton(title:title,font: UIFont.font(size: 16,weight: .regular),titleColor:.white,corner: 24,action: action)
-
-        btn.setTitleImageSpace(spacing: 4)
-        return btn
-    }
-
 
     func setUpGenerate() {
-        button = kCreateNormalSubmitBtn(title: "Create Now", action: { [weak self]  in
+        button = kCreateNormalSubmitBtn(
+            title: "Create Now".localized,
+            frame: CGRectMake(0, 0, k_ScreenWidth - 32, 48),
+            action: { [weak self]  in
             guard let self = self else { return }
             if let vc = WindowHelper.getCurrentViewController() {
                 if kPurchaseToolShared.kJudgeVipFreeType(vipFreeNumType: vipFreeNumType, vc: vc){ return }
@@ -150,8 +144,6 @@ extension TSAppBtnView{
 
             clickBlock?()
         })
-        button.frame = CGRectMake(0, 0, k_ScreenWidth - 32, 48)
-        button.addGradientBg(colors: ["#E961F6".uiColor.cgColor,"#7E57F4".uiColor.cgColor])
         contentView.addSubview(button)
         button.snp.makeConstraints { make in
             make.center.equalToSuperview()
@@ -164,12 +156,10 @@ extension TSAppBtnView{
     }
     
     func setUpThemeSet() {
-        button = kCreateNormalSubmitBtn(title: "Set Now", action: { [weak self]  in
+        button = kCreateNormalSubmitBtn(title: "Set Now".localized, frame: CGRectMake(0, 0, 200, 48),action: { [weak self]  in
             guard let self = self else { return }
             clickBlock?()
         })
-        button.frame = CGRectMake(0, 0, 200, 48)
-        button.addGradientBg(colors: ["#E961F6".uiColor.cgColor,"#7E57F4".uiColor.cgColor])
         contentView.addSubview(button)
         button.snp.makeConstraints { make in
             make.center.equalToSuperview()
@@ -179,3 +169,14 @@ extension TSAppBtnView{
    
     }
 }
+
+//常用提交按钮
+func kCreateNormalSubmitBtn(title:String,frame:CGRect,action: (() -> Void)? = nil) -> UIButton {
+    let btn = TSNormalSubmitBtn()
+    btn.setUpButton(title:title,font: UIFont.font(size: 16,weight: .regular),titleColor:.white,corner: 24,action: action)
+    btn.setTitleImageSpace(spacing: 4)
+    btn.frame = frame
+    btn.addGradientBg(colors: ["#E961F6".uiColor.cgColor,"#7E57F4".uiColor.cgColor])
+    return btn
+}
+

+ 6 - 0
AIRingtone/Common/NetworkManager/TSNetWork/TSNetWork+Business.swift

@@ -19,6 +19,12 @@ enum TSNeURLType:String {
     case themes = "/api/ops/themes"              //主体查询
     case musicCreate = "/api/music/create"       //铃声创建
     
+    
+    
+    case ringCategories = "/api/ops/ring-categories"     //铃声分类接口
+    case rings = "/api/ops/rings"                       //铃声分类下内容接口
+    case ringRecommend = "/api/ops/ring-recommend"       //铃声内容推荐接口
+    
     func getUrlString() -> String {
         return baseURL + self.rawValue
     }

+ 3 - 1
AIRingtone/Common/NetworkManager/TSNetWork/TSNetworkManager.swift

@@ -146,8 +146,10 @@ extension TSNetworkManager {
     func getRequest(url: String,parameters: [String: Any]? = nil) -> URLRequest? {
         // 构建 URL
         var components = URLComponents(string: url)
+        
         if let parameters = parameters {
-            components?.queryItems = parameters.map { URLQueryItem(name: $0.key, value: "\($0.value)") }
+            let sortedParameters = parameters.sorted { $0.key < $1.key } // 1. 对字典的键值对按键名进行排序
+            components?.queryItems = sortedParameters.map { URLQueryItem(name: $0.key, value: "\($0.value)") }
         }
         
         guard let url = components?.url else {

+ 1 - 1
AIRingtone/Common/Tool/TSBandRingTool/TSBandRingTool.swift

@@ -44,7 +44,7 @@ class TSBandRingTool:NSObject {
 
     func shareBand(with fileURLString: String,
                    fileName:String?,
-                   completion: ((Bool) -> Void)?) {
+                   completion: ((Bool) -> Void)? = nil) {
         
         if checkGarageBandInstallation() == false {
             completion?(false)