Browse Source

2.0(1)完成图生图的开发,并提测

100Years 1 month ago
parent
commit
5bda804d91
92 changed files with 2447 additions and 144 deletions
  1. 136 4
      AIEmoji.xcodeproj/project.pbxproj
  2. 2 2
      AIEmoji/Assets.xcassets/Common/add.imageset/Contents.json
  3. BIN
      AIEmoji/Assets.xcassets/Common/add.imageset/add@2x.png
  4. BIN
      AIEmoji/Assets.xcassets/Common/add.imageset/add@3x.png
  5. 22 0
      AIEmoji/Assets.xcassets/Common/clear_text.imageset/Contents.json
  6. BIN
      AIEmoji/Assets.xcassets/Common/clear_text.imageset/clear_text@2x.png
  7. BIN
      AIEmoji/Assets.xcassets/Common/clear_text.imageset/clear_text@3x.png
  8. 22 0
      AIEmoji/Assets.xcassets/Common/delete_redRound.imageset/Contents.json
  9. BIN
      AIEmoji/Assets.xcassets/Common/delete_redRound.imageset/delete_redRound@2x.png
  10. BIN
      AIEmoji/Assets.xcassets/Common/delete_redRound.imageset/delete_redRound@3x.png
  11. 0 0
      AIEmoji/Assets.xcassets/Common/launch_icon.imageset/Contents.json
  12. 0 0
      AIEmoji/Assets.xcassets/Common/launch_icon.imageset/launch_image@2x.png
  13. 0 0
      AIEmoji/Assets.xcassets/Common/launch_icon.imageset/launch_image@3x.png
  14. BIN
      AIEmoji/Assets.xcassets/Common/launch_text.imageset/launch_text@2x.png
  15. BIN
      AIEmoji/Assets.xcassets/Common/launch_text.imageset/launch_text@3x.png
  16. BIN
      AIEmoji/Assets.xcassets/Common/page_left.imageset/page_left@2x.png
  17. BIN
      AIEmoji/Assets.xcassets/Common/page_left.imageset/page_left@3x.png
  18. BIN
      AIEmoji/Assets.xcassets/Common/page_right.imageset/page_right@2x.png
  19. BIN
      AIEmoji/Assets.xcassets/Common/page_right.imageset/page_right@3x.png
  20. 12 0
      AIEmoji/Assets.xcassets/Common/rotating_animation.dataset/Contents.json
  21. BIN
      AIEmoji/Assets.xcassets/Common/rotating_animation.dataset/rotating_animation.gif
  22. 6 0
      AIEmoji/Assets.xcassets/PTP/Contents.json
  23. 22 0
      AIEmoji/Assets.xcassets/PTP/ptp_banner.imageset/Contents.json
  24. BIN
      AIEmoji/Assets.xcassets/PTP/ptp_banner.imageset/ptp_banner@2x.png
  25. BIN
      AIEmoji/Assets.xcassets/PTP/ptp_banner.imageset/ptp_banner@3x.png
  26. 22 0
      AIEmoji/Assets.xcassets/PTP/ptp_selected_border.imageset/Contents.json
  27. BIN
      AIEmoji/Assets.xcassets/PTP/ptp_selected_border.imageset/ptp_selected_border@2x.png
  28. BIN
      AIEmoji/Assets.xcassets/PTP/ptp_selected_border.imageset/ptp_selected_border@3x.png
  29. 22 0
      AIEmoji/Assets.xcassets/PTP/ptp_upload_bg.imageset/Contents.json
  30. BIN
      AIEmoji/Assets.xcassets/PTP/ptp_upload_bg.imageset/ptp_upload_bg@2x.png
  31. BIN
      AIEmoji/Assets.xcassets/PTP/ptp_upload_bg.imageset/ptp_upload_bg@3x.png
  32. 6 0
      AIEmoji/Assets.xcassets/PTP/style/Contents.json
  33. 22 0
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_0.imageset/Contents.json
  34. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_0.imageset/ptp_style_retro@2x.png
  35. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_0.imageset/ptp_style_retro@3x.png
  36. 22 0
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_1.imageset/Contents.json
  37. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_1.imageset/ptp_style_1@2x.png
  38. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_1.imageset/ptp_style_1@3x.png
  39. 22 0
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_2.imageset/Contents.json
  40. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_2.imageset/ptp_style_2@2x.png
  41. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_2.imageset/ptp_style_2@3x.png
  42. 22 0
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_3.imageset/Contents.json
  43. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_3.imageset/ptp_style_3@2x.png
  44. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_3.imageset/ptp_style_3@3x.png
  45. 22 0
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_4.imageset/Contents.json
  46. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_4.imageset/ptp_style_4@2x.png
  47. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_4.imageset/ptp_style_4@3x.png
  48. 22 0
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_5.imageset/Contents.json
  49. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_5.imageset/ptp_style_5@2x.png
  50. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_5.imageset/ptp_style_5@3x.png
  51. 22 0
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_6.imageset/Contents.json
  52. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_6.imageset/ptp_style_6@2x.png
  53. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_6.imageset/ptp_style_6@3x.png
  54. 22 0
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_7.imageset/Contents.json
  55. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_7.imageset/ptp_style_7@2x.png
  56. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_7.imageset/ptp_style_7@3x.png
  57. 22 0
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_8.imageset/Contents.json
  58. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_8.imageset/ptp_style_8@2x.png
  59. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_8.imageset/ptp_style_8@3x.png
  60. 22 0
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_9.imageset/Contents.json
  61. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_9.imageset/ptp_style_9@2x.png
  62. BIN
      AIEmoji/Assets.xcassets/PTP/style/ptp_style_9.imageset/ptp_style_9@3x.png
  63. 2 2
      AIEmoji/Base.lproj/LaunchScreen.storyboard
  64. 1 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputBarVC.swift
  65. 1 1
      AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSAIChatVM.swift
  66. 7 0
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSCellView/Untitled.swift
  67. 5 5
      AIEmoji/Business/TSGenmojiVC/TSGenmojiGennerateVC/TSGenmojiGennerateViewModel.swift
  68. 2 0
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/Model/TSGenmojiModel.swift
  69. 7 40
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/TSGenmojiVC.swift
  70. 9 0
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiColSectionView.swift
  71. 23 35
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiGennerateCell.swift
  72. 38 6
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/ViewModel/TSGenmojiCollectionViewModel.swift
  73. 259 0
      AIEmoji/Business/TSPTPGeneratorVC/TSPTPBrowseVC/TSPTPBrowseVC.swift
  74. 236 0
      AIEmoji/Business/TSPTPGeneratorVC/TSPTPGeneratorVC/TSPTPGeneratorVC.swift
  75. 146 0
      AIEmoji/Business/TSPTPGeneratorVC/TSPTPGeneratorVC/VM/TSPTPGeneratorVM.swift
  76. 28 0
      AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/Cell/TSPTPGeneratorCell.swift
  77. 147 0
      AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/Cell/TSPTPSelectStyleCell.swift
  78. 125 0
      AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/Cell/TSPTPUploadCell.swift
  79. 24 0
      AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/M/TSPTPStyleModel.swift
  80. 62 0
      AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/M/photo_to_photo_style.json
  81. 213 0
      AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/TSPhotoToPhotoVC.swift
  82. 223 0
      AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/VM/TSPhotoToPhotoVM.swift
  83. 23 18
      AIEmoji/Business/TSTextGeneralPictureVC/TSTextGeneralPictureVC/TSTextGeneralPictureVC.swift
  84. 16 0
      AIEmoji/Business/TSTextGeneralPictureVC/TSTextGeneralPictureVC/ViewModel/TSTextGeneralPictureVM.swift
  85. 2 2
      AIEmoji/Business/TSTextGeneralPictureVC/TSTextPicGennerateVC/TSTextPicGennerateVM.swift
  86. 67 14
      AIEmoji/Business/VIewTool/TSCommonloadingView.swift
  87. 31 8
      AIEmoji/Common/NetworkManager/TSNetWork/TSNetWork+Business.swift
  88. 2 2
      AIEmoji/Common/NetworkManager/TSNetWork/TSNetworkManager+Loading.swift
  89. 116 3
      AIEmoji/Common/NetworkManager/TSNetWork/TSNetworkManager.swift
  90. 5 2
      AIEmoji/Common/Purchase/TSPurchaseManager.swift
  91. 135 0
      AIEmoji/Common/View/TSPhotoPickerManager/TSPhotoPickerManager.swift
  92. BIN
      AIEmoji/Res/rotatingAnimation.gif

+ 136 - 4
AIEmoji.xcodeproj/project.pbxproj

@@ -72,6 +72,19 @@
 		A80EDD682D6C5098003CD332 /* TSChatMsgBaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDD672D6C507D003CD332 /* TSChatMsgBaseView.swift */; };
 		A80EDD6A2D6C518E003CD332 /* TSChatMsgToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDD692D6C5176003CD332 /* TSChatMsgToolView.swift */; };
 		A80EDDD92D6D9713003CD332 /* TSCustomStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDDD82D6D9713003CD332 /* TSCustomStackView.swift */; };
+		A80EDDDB2D6EB0D0003CD332 /* Untitled.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDDDA2D6EB0BB003CD332 /* Untitled.swift */; };
+		A80EDDE02D6EB1B9003CD332 /* TSPTPGeneratorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDDDF2D6EB1B8003CD332 /* TSPTPGeneratorCell.swift */; };
+		A80EDDE22D6EB8D8003CD332 /* TSPTPUploadCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDDE12D6EB8C7003CD332 /* TSPTPUploadCell.swift */; };
+		A80EDDE42D6EB8FA003CD332 /* TSPTPSelectStyleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDDE32D6EB8DC003CD332 /* TSPTPSelectStyleCell.swift */; };
+		A80EDDE72D6EBFC1003CD332 /* TSPTPGeneratorVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDDE62D6EBFBE003CD332 /* TSPTPGeneratorVM.swift */; };
+		A80EDDEB2D6EC014003CD332 /* TSPhotoToPhotoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDDEA2D6EC012003CD332 /* TSPhotoToPhotoVC.swift */; };
+		A80EDDF02D6EC045003CD332 /* TSPTPStyleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDDEF2D6EC044003CD332 /* TSPTPStyleModel.swift */; };
+		A80EDDF62D6EC1ED003CD332 /* TSPhotoToPhotoVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDDF52D6EC1E8003CD332 /* TSPhotoToPhotoVM.swift */; };
+		A80EDDFD2D6EF34F003CD332 /* photo_to_photo_style.json in Resources */ = {isa = PBXBuildFile; fileRef = A80EDDFC2D6EF34C003CD332 /* photo_to_photo_style.json */; };
+		A80EDE002D6EFD22003CD332 /* TSPhotoPickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDDFF2D6EFD20003CD332 /* TSPhotoPickerManager.swift */; };
+		A80EDE022D6F1CCD003CD332 /* TSPTPGeneratorVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDE012D6F1CCB003CD332 /* TSPTPGeneratorVC.swift */; };
+		A80EDE062D6F3491003CD332 /* TSPTPBrowseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDE052D6F3490003CD332 /* TSPTPBrowseVC.swift */; };
+		A80EDE082D700395003CD332 /* rotatingAnimation.gif in Resources */ = {isa = PBXBuildFile; fileRef = A80EDE072D700395003CD332 /* rotatingAnimation.gif */; };
 		A85E478F2D67115A0018D62D /* TSTextGeneralPictureVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A85E478E2D6711590018D62D /* TSTextGeneralPictureVC.swift */; };
 		A85E47922D6728A00018D62D /* TSTextGeneralPictureVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A85E47912D67289F0018D62D /* TSTextGeneralPictureVM.swift */; };
 		A85E47962D672ADA0018D62D /* TSTextPicGennerateVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A85E47952D672AD90018D62D /* TSTextPicGennerateVC.swift */; };
@@ -241,6 +254,19 @@
 		A80EDD672D6C507D003CD332 /* TSChatMsgBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatMsgBaseView.swift; sourceTree = "<group>"; };
 		A80EDD692D6C5176003CD332 /* TSChatMsgToolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatMsgToolView.swift; sourceTree = "<group>"; };
 		A80EDDD82D6D9713003CD332 /* TSCustomStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSCustomStackView.swift; sourceTree = "<group>"; };
+		A80EDDDA2D6EB0BB003CD332 /* Untitled.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Untitled.swift; sourceTree = "<group>"; };
+		A80EDDDF2D6EB1B8003CD332 /* TSPTPGeneratorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPTPGeneratorCell.swift; sourceTree = "<group>"; };
+		A80EDDE12D6EB8C7003CD332 /* TSPTPUploadCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPTPUploadCell.swift; sourceTree = "<group>"; };
+		A80EDDE32D6EB8DC003CD332 /* TSPTPSelectStyleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPTPSelectStyleCell.swift; sourceTree = "<group>"; };
+		A80EDDE62D6EBFBE003CD332 /* TSPTPGeneratorVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPTPGeneratorVM.swift; sourceTree = "<group>"; };
+		A80EDDEA2D6EC012003CD332 /* TSPhotoToPhotoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPhotoToPhotoVC.swift; sourceTree = "<group>"; };
+		A80EDDEF2D6EC044003CD332 /* TSPTPStyleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPTPStyleModel.swift; sourceTree = "<group>"; };
+		A80EDDF52D6EC1E8003CD332 /* TSPhotoToPhotoVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPhotoToPhotoVM.swift; sourceTree = "<group>"; };
+		A80EDDFC2D6EF34C003CD332 /* photo_to_photo_style.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = photo_to_photo_style.json; sourceTree = "<group>"; };
+		A80EDDFF2D6EFD20003CD332 /* TSPhotoPickerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPhotoPickerManager.swift; sourceTree = "<group>"; };
+		A80EDE012D6F1CCB003CD332 /* TSPTPGeneratorVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPTPGeneratorVC.swift; sourceTree = "<group>"; };
+		A80EDE052D6F3490003CD332 /* TSPTPBrowseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPTPBrowseVC.swift; sourceTree = "<group>"; };
+		A80EDE072D700395003CD332 /* rotatingAnimation.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = rotatingAnimation.gif; sourceTree = "<group>"; };
 		A85E478E2D6711590018D62D /* TSTextGeneralPictureVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextGeneralPictureVC.swift; sourceTree = "<group>"; };
 		A85E47912D67289F0018D62D /* TSTextGeneralPictureVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextGeneralPictureVM.swift; sourceTree = "<group>"; };
 		A85E47952D672AD90018D62D /* TSTextPicGennerateVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextPicGennerateVC.swift; sourceTree = "<group>"; };
@@ -806,6 +832,95 @@
 			path = UIStackView;
 			sourceTree = "<group>";
 		};
+		A80EDDDC2D6EB17D003CD332 /* TSPTPGeneratorVC */ = {
+			isa = PBXGroup;
+			children = (
+				A80EDE042D6F3429003CD332 /* TSPTPBrowseVC */,
+				A80EDDE92D6EBFF1003CD332 /* TSPhotoToPhotoVC */,
+				A80EDDDD2D6EB19A003CD332 /* TSPTPGeneratorVC */,
+			);
+			path = TSPTPGeneratorVC;
+			sourceTree = "<group>";
+		};
+		A80EDDDD2D6EB19A003CD332 /* TSPTPGeneratorVC */ = {
+			isa = PBXGroup;
+			children = (
+				A80EDE012D6F1CCB003CD332 /* TSPTPGeneratorVC.swift */,
+				A80EDDE82D6EBFCD003CD332 /* M */,
+				A80EDDE52D6EBFAE003CD332 /* VM */,
+			);
+			path = TSPTPGeneratorVC;
+			sourceTree = "<group>";
+		};
+		A80EDDDE2D6EB1A5003CD332 /* Cell */ = {
+			isa = PBXGroup;
+			children = (
+				A80EDDE32D6EB8DC003CD332 /* TSPTPSelectStyleCell.swift */,
+				A80EDDE12D6EB8C7003CD332 /* TSPTPUploadCell.swift */,
+				A80EDDDF2D6EB1B8003CD332 /* TSPTPGeneratorCell.swift */,
+			);
+			path = Cell;
+			sourceTree = "<group>";
+		};
+		A80EDDE52D6EBFAE003CD332 /* VM */ = {
+			isa = PBXGroup;
+			children = (
+				A80EDDE62D6EBFBE003CD332 /* TSPTPGeneratorVM.swift */,
+			);
+			path = VM;
+			sourceTree = "<group>";
+		};
+		A80EDDE82D6EBFCD003CD332 /* M */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			path = M;
+			sourceTree = "<group>";
+		};
+		A80EDDE92D6EBFF1003CD332 /* TSPhotoToPhotoVC */ = {
+			isa = PBXGroup;
+			children = (
+				A80EDDED2D6EC020003CD332 /* M */,
+				A80EDDEC2D6EC01B003CD332 /* VM */,
+				A80EDDDE2D6EB1A5003CD332 /* Cell */,
+				A80EDDEA2D6EC012003CD332 /* TSPhotoToPhotoVC.swift */,
+			);
+			path = TSPhotoToPhotoVC;
+			sourceTree = "<group>";
+		};
+		A80EDDEC2D6EC01B003CD332 /* VM */ = {
+			isa = PBXGroup;
+			children = (
+				A80EDDF52D6EC1E8003CD332 /* TSPhotoToPhotoVM.swift */,
+			);
+			path = VM;
+			sourceTree = "<group>";
+		};
+		A80EDDED2D6EC020003CD332 /* M */ = {
+			isa = PBXGroup;
+			children = (
+				A80EDDFC2D6EF34C003CD332 /* photo_to_photo_style.json */,
+				A80EDDEF2D6EC044003CD332 /* TSPTPStyleModel.swift */,
+			);
+			path = M;
+			sourceTree = "<group>";
+		};
+		A80EDDFE2D6EFD1A003CD332 /* TSPhotoPickerManager */ = {
+			isa = PBXGroup;
+			children = (
+				A80EDDFF2D6EFD20003CD332 /* TSPhotoPickerManager.swift */,
+			);
+			path = TSPhotoPickerManager;
+			sourceTree = "<group>";
+		};
+		A80EDE042D6F3429003CD332 /* TSPTPBrowseVC */ = {
+			isa = PBXGroup;
+			children = (
+				A80EDE052D6F3490003CD332 /* TSPTPBrowseVC.swift */,
+			);
+			path = TSPTPBrowseVC;
+			sourceTree = "<group>";
+		};
 		A85E478D2D670DF10018D62D /* TSTextGeneralPictureVC */ = {
 			isa = PBXGroup;
 			children = (
@@ -853,6 +968,7 @@
 		A85E47C12D6964500018D62D /* TSCellView */ = {
 			isa = PBXGroup;
 			children = (
+				A80EDDDA2D6EB0BB003CD332 /* Untitled.swift */,
 				A80EDD672D6C507D003CD332 /* TSChatMsgBaseView.swift */,
 				A85E47C22D69646D0018D62D /* TSMSGAIDefaultHeaderView.swift */,
 				A80EDD692D6C5176003CD332 /* TSChatMsgToolView.swift */,
@@ -1017,6 +1133,7 @@
 		A8F774922D38EA8C00AA6E93 /* Business */ = {
 			isa = PBXGroup;
 			children = (
+				A80EDDDC2D6EB17D003CD332 /* TSPTPGeneratorVC */,
 				A85E478D2D670DF10018D62D /* TSTextGeneralPictureVC */,
 				A80E74222D5996BF00C64288 /* AIChat */,
 				A80E73DD2D533E5800C64288 /* TSPurchaseMembershipVC */,
@@ -1080,6 +1197,7 @@
 		A8F774D72D38EA8C00AA6E93 /* View */ = {
 			isa = PBXGroup;
 			children = (
+				A80EDDFE2D6EFD1A003CD332 /* TSPhotoPickerManager */,
 				A80EDDD72D6D9705003CD332 /* UIStackView */,
 				A8F776292D3A70AA00AA6E93 /* UILabel */,
 			);
@@ -1346,6 +1464,7 @@
 		A8FB02AE2D3E38FA0031A396 /* Res */ = {
 			isa = PBXGroup;
 			children = (
+				A80EDE072D700395003CD332 /* rotatingAnimation.gif */,
 				A89EA6972D5B19F0000EB181 /* Butterfly🦋.json */,
 				A80E723E2D3F4D3000C64288 /* Universe🌍.json */,
 				A80E723B2D3F4CA700C64288 /* Food🍔.json */,
@@ -1451,8 +1570,10 @@
 				A8EEADE02D3E75900032C5A0 /* Crystal🔮.json in Resources */,
 				A80E723C2D3F4CAA00C64288 /* Food🍔.json in Resources */,
 				A8EEADE72D3E76860032C5A0 /* Drink🥤.json in Resources */,
+				A80EDE082D700395003CD332 /* rotatingAnimation.gif in Resources */,
 				A8FB02D12D3E6B2A0031A396 /* Cat🐱.json in Resources */,
 				A80E72462D3F4EED00C64288 /* sticker.json in Resources */,
+				A80EDDFD2D6EF34F003CD332 /* photo_to_photo_style.json in Resources */,
 				A8EEADD62D3E6CD80032C5A0 /* Fish🐠.json in Resources */,
 				A8EEADE22D3E76110032C5A0 /* Dog🐕.json in Resources */,
 				A8EEADD82D3E74D20032C5A0 /* Pink🩷.json in Resources */,
@@ -1515,6 +1636,7 @@
 				A8F775252D38ED8300AA6E93 /* TSConfig.swift in Sources */,
 				A80E726C2D409E8300C64288 /* TSDiyTLPinkAnimalView.swift in Sources */,
 				A80E72652D409B0D00C64288 /* DiyStaticElement.swift in Sources */,
+				A80EDE002D6EFD22003CD332 /* TSPhotoPickerManager.swift in Sources */,
 				A89EA6B42D5C9D43000EB181 /* TSAIChatHistoryVM.swift in Sources */,
 				A80E72532D3F985E00C64288 /* TSWallpaperVC.swift in Sources */,
 				A8FB02B32D3E39A40031A396 /* TSEmojisModel.swift in Sources */,
@@ -1533,6 +1655,7 @@
 				A89EA6CC2D642CE2000EB181 /* TSChatViewController+NaviBar.swift in Sources */,
 				A8F7753B2D3918DE00AA6E93 /* TSNetworkManager+Loading.swift in Sources */,
 				A80E721A2D3F393A00C64288 /* DiyStickerModel.swift in Sources */,
+				A80EDDEB2D6EC014003CD332 /* TSPhotoToPhotoVC.swift in Sources */,
 				A80E726F2D40DE2B00C64288 /* TSWallpaperPreviewVC.swift in Sources */,
 				A8F775492D3935D600AA6E93 /* TSBusinessWebVC.swift in Sources */,
 				A8F776392D3B38E600AA6E93 /* TSGenmojiGennerateVC.swift in Sources */,
@@ -1569,6 +1692,7 @@
 				A80EDD5D2D6C3F82003CD332 /* MarkdownCodeEscaping.swift in Sources */,
 				A80EDD5E2D6C3F82003CD332 /* MarkdownLinkElement.swift in Sources */,
 				A80EDD5F2D6C3F82003CD332 /* MarkdownHeader+UIKit.swift in Sources */,
+				A80EDE062D6F3491003CD332 /* TSPTPBrowseVC.swift in Sources */,
 				A80EDD6A2D6C518E003CD332 /* TSChatMsgToolView.swift in Sources */,
 				A80EDD602D6C3F82003CD332 /* MarkdownElement.swift in Sources */,
 				A80EDD612D6C3F82003CD332 /* MarkdownLink+AppKit.swift in Sources */,
@@ -1595,9 +1719,12 @@
 				A80E722F2D3F3E1400C64288 /* TSDiyCanvasView.swift in Sources */,
 				A80E72672D409C7D00C64288 /* Template+More.swift in Sources */,
 				A89EA6AC2D5B3EFB000EB181 /* TSRealmManager.swift in Sources */,
+				A80EDDE42D6EB8FA003CD332 /* TSPTPSelectStyleCell.swift in Sources */,
 				A8F775172D38EB7400AA6E93 /* TSTabBarController.swift in Sources */,
 				A8F776272D3A6EC200AA6E93 /* TSGenmojiTextView.swift in Sources */,
 				A80E73E12D533E5800C64288 /* TSPurchaseVC.swift in Sources */,
+				A80EDDE02D6EB1B9003CD332 /* TSPTPGeneratorCell.swift in Sources */,
+				A80EDDDB2D6EB0D0003CD332 /* Untitled.swift in Sources */,
 				A8F776352D3A7C2B00AA6E93 /* TSGenmojiColSectionView.swift in Sources */,
 				A80E724F2D3F6D7F00C64288 /* DiyFixedTextElement.swift in Sources */,
 				A85E478F2D67115A0018D62D /* TSTextGeneralPictureVC.swift in Sources */,
@@ -1613,6 +1740,7 @@
 				A80E72772D41EFF900C64288 /* TSEmojisTutorialsVC.swift in Sources */,
 				A8F776482D3DE9F600AA6E93 /* TSSmallIconBrowseCell.swift in Sources */,
 				A8F7753D2D3918F800AA6E93 /* TSNetWork+Business.swift in Sources */,
+				A80EDE022D6F1CCD003CD332 /* TSPTPGeneratorVC.swift in Sources */,
 				A85E479F2D6859FA0018D62D /* TSRandomTextPicker.swift in Sources */,
 				A80E72262D3F3A9A00C64288 /* HYHAddImageView.m in Sources */,
 				A80E72272D3F3A9A00C64288 /* DiyTextElement.swift in Sources */,
@@ -1621,6 +1749,7 @@
 				A80E72562D3F98D700C64288 /* TSDiyKeyboardViewVC.swift in Sources */,
 				A8F775032D38EA8C00AA6E93 /* GlobalImports.swift in Sources */,
 				A89EA67A2D59D25F000EB181 /* TSAIChatVM.swift in Sources */,
+				A80EDDF02D6EC045003CD332 /* TSPTPStyleModel.swift in Sources */,
 				A85E47BE2D68999B0018D62D /* ShareActivityItemProvider.swift in Sources */,
 				A8F7763C2D3B429B00AA6E93 /* TSCommonloadingView.swift in Sources */,
 				A8F776322D3A771400AA6E93 /* TSGenmojiCollectionViewModel.swift in Sources */,
@@ -1640,8 +1769,11 @@
 				A8F7762B2D3A70B200AA6E93 /* PaddedLabel.swift in Sources */,
 				A80E73E62D5348D000C64288 /* SettingPurchaseTopView.swift in Sources */,
 				A80EDDD92D6D9713003CD332 /* TSCustomStackView.swift in Sources */,
+				A80EDDE22D6EB8D8003CD332 /* TSPTPUploadCell.swift in Sources */,
+				A80EDDF62D6EC1ED003CD332 /* TSPhotoToPhotoVM.swift in Sources */,
 				A80E72382D3F473B00C64288 /* DiyPaperProtocol.swift in Sources */,
 				A8F775382D390C3C00AA6E93 /* TSNetworkManager.swift in Sources */,
+				A80EDDE72D6EBFC1003CD332 /* TSPTPGeneratorVM.swift in Sources */,
 				A85E47982D672AE70018D62D /* TSTextPicGennerateVM.swift in Sources */,
 				A89EA65F2D59AA11000EB181 /* TSChatViewController.swift in Sources */,
 				A89EA6C62D5F5C22000EB181 /* TSChatInputFullScreenVC.swift in Sources */,
@@ -1676,7 +1808,7 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 3;
+				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = 65UD255J84;
 				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -1692,7 +1824,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 1.9;
+				MARKETING_VERSION = 2.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.girl.music.wallpaper;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@@ -1715,7 +1847,7 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 3;
+				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = 65UD255J84;
 				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -1731,7 +1863,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 1.9;
+				MARKETING_VERSION = 2.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.girl.music.wallpaper;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";

+ 2 - 2
AIEmoji/Assets.xcassets/Common/launch_text.imageset/Contents.json → AIEmoji/Assets.xcassets/Common/add.imageset/Contents.json

@@ -5,12 +5,12 @@
       "scale" : "1x"
     },
     {
-      "filename" : "launch_text@2x.png",
+      "filename" : "add@2x.png",
       "idiom" : "universal",
       "scale" : "2x"
     },
     {
-      "filename" : "launch_text@3x.png",
+      "filename" : "add@3x.png",
       "idiom" : "universal",
       "scale" : "3x"
     }

BIN
AIEmoji/Assets.xcassets/Common/add.imageset/add@2x.png


BIN
AIEmoji/Assets.xcassets/Common/add.imageset/add@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/Common/clear_text.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/Common/clear_text.imageset/clear_text@2x.png


BIN
AIEmoji/Assets.xcassets/Common/clear_text.imageset/clear_text@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/Common/delete_redRound.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/Common/delete_redRound.imageset/delete_redRound@2x.png


BIN
AIEmoji/Assets.xcassets/Common/delete_redRound.imageset/delete_redRound@3x.png


+ 0 - 0
AIEmoji/Assets.xcassets/Common/launch_image.imageset/Contents.json → AIEmoji/Assets.xcassets/Common/launch_icon.imageset/Contents.json


+ 0 - 0
AIEmoji/Assets.xcassets/Common/launch_image.imageset/launch_image@2x.png → AIEmoji/Assets.xcassets/Common/launch_icon.imageset/launch_image@2x.png


+ 0 - 0
AIEmoji/Assets.xcassets/Common/launch_image.imageset/launch_image@3x.png → AIEmoji/Assets.xcassets/Common/launch_icon.imageset/launch_image@3x.png


BIN
AIEmoji/Assets.xcassets/Common/launch_text.imageset/launch_text@2x.png


BIN
AIEmoji/Assets.xcassets/Common/launch_text.imageset/launch_text@3x.png


BIN
AIEmoji/Assets.xcassets/Common/page_left.imageset/page_left@2x.png


BIN
AIEmoji/Assets.xcassets/Common/page_left.imageset/page_left@3x.png


BIN
AIEmoji/Assets.xcassets/Common/page_right.imageset/page_right@2x.png


BIN
AIEmoji/Assets.xcassets/Common/page_right.imageset/page_right@3x.png


+ 12 - 0
AIEmoji/Assets.xcassets/Common/rotating_animation.dataset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "data" : [
+    {
+      "filename" : "rotating_animation.gif",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIEmoji/Assets.xcassets/Common/rotating_animation.dataset/rotating_animation.gif


+ 6 - 0
AIEmoji/Assets.xcassets/PTP/Contents.json

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

+ 22 - 0
AIEmoji/Assets.xcassets/PTP/ptp_banner.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/ptp_banner.imageset/ptp_banner@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/ptp_banner.imageset/ptp_banner@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/ptp_selected_border.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/ptp_selected_border.imageset/ptp_selected_border@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/ptp_selected_border.imageset/ptp_selected_border@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/ptp_upload_bg.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/ptp_upload_bg.imageset/ptp_upload_bg@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/ptp_upload_bg.imageset/ptp_upload_bg@3x.png


+ 6 - 0
AIEmoji/Assets.xcassets/PTP/style/Contents.json

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

+ 22 - 0
AIEmoji/Assets.xcassets/PTP/style/ptp_style_0.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_0.imageset/ptp_style_retro@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_0.imageset/ptp_style_retro@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/style/ptp_style_1.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_1.imageset/ptp_style_1@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_1.imageset/ptp_style_1@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/style/ptp_style_2.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_2.imageset/ptp_style_2@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_2.imageset/ptp_style_2@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/style/ptp_style_3.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_3.imageset/ptp_style_3@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_3.imageset/ptp_style_3@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/style/ptp_style_4.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_4.imageset/ptp_style_4@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_4.imageset/ptp_style_4@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/style/ptp_style_5.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_5.imageset/ptp_style_5@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_5.imageset/ptp_style_5@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/style/ptp_style_6.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_6.imageset/ptp_style_6@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_6.imageset/ptp_style_6@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/style/ptp_style_7.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_7.imageset/ptp_style_7@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_7.imageset/ptp_style_7@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/style/ptp_style_8.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_8.imageset/ptp_style_8@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_8.imageset/ptp_style_8@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/PTP/style/ptp_style_9.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_9.imageset/ptp_style_9@2x.png


BIN
AIEmoji/Assets.xcassets/PTP/style/ptp_style_9.imageset/ptp_style_9@3x.png


+ 2 - 2
AIEmoji/Base.lproj/LaunchScreen.storyboard

@@ -16,7 +16,7 @@
                         <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
-                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch_image" translatesAutoresizingMaskIntoConstraints="NO" id="FfL-7D-71P">
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch_icon" translatesAutoresizingMaskIntoConstraints="NO" id="FfL-7D-71P">
                                 <rect key="frame" x="65" y="325" width="263.33333333333331" height="62"/>
                             </imageView>
                         </subviews>
@@ -34,6 +34,6 @@
         </scene>
     </scenes>
     <resources>
-        <image name="launch_image" width="263.33334350585938" height="62"/>
+        <image name="launch_icon" width="263.33334350585938" height="62"/>
     </resources>
 </document>

+ 1 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputBarVC.swift

@@ -39,6 +39,7 @@ class TSChatInputBarVC: TSBaseVC, UITextViewDelegate {
             guard let self = self else { return }
             NotificationCenter.default.post(name: .kAIStopRespondNotification, object: nil, userInfo: nil)
         }
+        stopBtn.isHidden = true
         return stopBtn
     }()
     

+ 1 - 1
AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSAIChatVM.swift

@@ -21,7 +21,7 @@ class TSAIChatVM {
     }()
 
     var uiStyle:UIStype = UIStype.chat
-    var streamRequest:DataStreamRequest?
+    var streamRequest:Request?
     
     let kAIUser = TSChatUser(senderId: "000", displayName: "AI",idType: .aiRobot)
     let kUserSender = TSChatUser(senderId: "001", displayName: "001",idType: .user)

+ 7 - 0
AIEmoji/Business/AIChat/TSChatViewController/Views/TSCellView/Untitled.swift

@@ -0,0 +1,7 @@
+//
+//  Untitled.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/25.
+//
+

+ 5 - 5
AIEmoji/Business/TSGenmojiVC/TSGenmojiGennerateVC/TSGenmojiGennerateViewModel.swift

@@ -66,11 +66,11 @@ enum TSProgressState  {
             return false
         case .pending:
             return false
-        case .progress(let float):
+        case .progress(_):
             return false
-        case .success(let string):
+        case .success(_):
             return true
-        case .failed(let string):
+        case .failed(_):
             return true
         }
     }
@@ -90,8 +90,8 @@ enum TSProgressState  {
 
 class TSGenmojiGennerateViewModel {
 
-    var creatRequest:DataRequest?
-    var queryRequest:DataRequest?
+    var creatRequest:Request?
+    var queryRequest:Request?
     var stopNetwork = false
     
     @Published var stateDatauPblished:(TSProgressState,TSGenmojiModel?) = (TSProgressState.none,nil)

+ 2 - 0
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/Model/TSGenmojiModel.swift

@@ -48,10 +48,12 @@ class TSGenmojiModel: TSBaseModel {
 
 class TSGenmojiRequestModel : TSBaseModel {
     var prompt:String = ""
+    var promptSort:String = ""
     var width:Int = 0
     var height:Int = 0
     override func mapping(map: ObjectMapper.Map) {
         prompt              <- map["prompt"]
+        promptSort          <- map["promptSort"]
         width               <- map["width"]
         height              <- map["height"]
     }

+ 7 - 40
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/TSGenmojiVC.swift

@@ -19,40 +19,6 @@ class TSGenmojiVC: TSBaseVC {
         return vipBtn
     }()
     
-//    lazy var navBarView: TSBaseNavContentBarView = {
-//        let navBarView = TSBaseNavContentBarView()
-//        
-//        let titleImageView = UIImageView.createImageView(imageName: "nav_title_genmoji",contentMode: .scaleToFill)
-//        navBarView.barView.addSubview(titleImageView)
-//        titleImageView.snp.makeConstraints { make in
-//            make.centerY.equalToSuperview()
-//            make.left.equalTo(16)
-//        }
-//        
-//        let setBtn = UIButton.createButton(image: UIImage(named: "setting")) { [weak self]  in
-//            guard let self = self else { return }
-//            let setingVC = TSSetingVC()
-//            setingVC.hidesBottomBarWhenPushed = true
-//            navigationController?.pushViewController(setingVC, animated: true)
-//        }
-//        navBarView.barView.addSubview(setBtn)
-//        setBtn.snp.makeConstraints { make in
-//            make.centerY.equalToSuperview()
-//            make.trailing.equalTo(-16)
-//            make.width.height.equalTo(24)
-//        }
-//        
-//        navBarView.barView.addSubview(vipBtn)
-//        vipBtn.snp.makeConstraints { make in
-//            make.centerY.equalToSuperview()
-//            make.trailing.equalTo(-62)
-//            make.width.height.equalTo(24)
-//        }
-//        
-//        return navBarView
-//    }()
-    
-    
     lazy var collectionComponent: TSCollectionViewComponent = {
         let layout = UICollectionViewFlowLayout()
         let cp = TSCollectionViewComponent(frame: CGRect.zero, layout: layout, attributes: [ :])
@@ -77,7 +43,6 @@ class TSGenmojiVC: TSBaseVC {
         
         cp.itemDidSelectedHandler = { [weak self] (object, indexPath) in
             guard let self = self else { return }
-            view.endEditing(true)
             if indexPath.section == 0{
                 return
             }
@@ -106,16 +71,14 @@ class TSGenmojiVC: TSBaseVC {
         addNormalNavBarView()
         setPageTitle("AI Text to Emoji".localized)
         
-//        navBarContentView.addSubview(navBarView)
-//        navBarView.snp.makeConstraints { make in
-//            make.edges.equalToSuperview()
-//        }
-        
         contentView.addSubview(collectionComponent.collectionView)
         collectionComponent.collectionView.snp.makeConstraints { make in
             make.edges.equalToSuperview()
         }
         
+        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(clickCollectionView))
+        tapGesture.cancelsTouchesInView = false // 确保不影响其他点击事件
+        collectionComponent.collectionView.addGestureRecognizer(tapGesture)
         
         collectionComponent.clear()
         collectionComponent.reloadView(with:viewModel.colDataArray)
@@ -132,6 +95,10 @@ class TSGenmojiVC: TSBaseVC {
             self.collectionComponent.reloadData()
         }
     }
+    
+    @objc func clickCollectionView() {
+        view.endEditing(true)
+    }
 }
 
 

+ 9 - 0
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiColSectionView.swift

@@ -41,6 +41,15 @@ class TSGenmojiColSectionView: TSBaseCollectionnReusableView {
         super.renderView(with: object, component: component, attributes: attributes)
         if let componentReuseViewModel = object as? TSGenmojiColComponentReuseViewModel {
             leftLab.text = componentReuseViewModel.sectionModel.name
+            
+            switch componentReuseViewModel.sectionModel.style {
+            case .history,.textPicHistory:
+                delBtn.isHidden = false
+            default:
+                delBtn.isHidden = true
+            }
+            
+        
         }
     }
     

+ 23 - 35
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiGennerateCell.swift

@@ -67,6 +67,19 @@ class TSGenmojiGennerateCell : TSBaseCollectionCell{
         return inspirationBtn
     }()
     
+    
+    lazy var clearBtn: UIButton = {
+        let clearBtn = UIButton.createButton(
+            image: UIImage(named: "clear_text")
+        )
+        { [weak self]  in
+            guard let self = self else { return }
+            customTextView.text = ""
+        }
+        clearBtn.isHidden = true
+        return clearBtn
+    }()
+    
     override func creatUI() {
         
         contentView.addSubview(robotImageView)
@@ -96,6 +109,13 @@ class TSGenmojiGennerateCell : TSBaseCollectionCell{
         inspirationBtn.snp.makeConstraints { make in
             make.height.equalTo(28)
             make.bottom.equalTo(-16)
+            make.leading.equalTo(16)
+        }
+        
+        textBgView.addSubview(clearBtn)
+        clearBtn.snp.makeConstraints { make in
+            make.width.height.equalTo(16)
+            make.bottom.equalTo(-16)
             make.trailing.equalTo(-16)
         }
         
@@ -107,40 +127,7 @@ class TSGenmojiGennerateCell : TSBaseCollectionCell{
             make.height.equalTo(48)
         }
     }
-//    override func creatUI() {
-//        
-//        contentView.addSubview(robotImageView)
-//        robotImageView.snp.makeConstraints { make in
-//            make.top.equalTo(0)
-//            make.leading.equalTo(0)
-//            make.width.height.equalTo(82)
-//        }
-//        
-//        contentView.addSubview(customTextView)
-//        customTextView.snp.makeConstraints { make in
-//            make.top.equalTo(57)
-//            make.leading.equalTo(0)
-//            make.trailing.equalTo(0)
-//            make.height.equalTo(182.0*kDesignScale)
-//        }
-//        
-//        contentView.addSubview(inspirationBtn)
-//        inspirationBtn.snp.makeConstraints { make in
-//            make.height.equalTo(28)
-//            make.bottom.equalTo(customTextView.snp.bottom).offset(-8)
-//            make.trailing.equalTo(customTextView.snp.trailing).offset(-8)
-//        }
-//        
-//        contentView.addSubview(submitBtn)
-//        submitBtn.snp.makeConstraints { make in
-//            make.top.equalTo(customTextView.snp.bottom).offset(16)
-//            make.leading.equalTo(0)
-//            make.trailing.equalTo(0)
-//            make.height.equalTo(48)
-//        }
-//    }
-    
-    
+
     func getVipText()->String{
         if kPurchaseDefault.isVip {
             return "Generate"
@@ -157,7 +144,7 @@ class TSGenmojiGennerateCell : TSBaseCollectionCell{
                 title = "Generate (\(kPurchaseDefault.freeNum(type: .generatePic)))"
                 inspirationBtn.isHidden = true
                 customTextView.snp.updateConstraints { make in
-                    make.bottom.equalTo(-21)
+                    make.bottom.equalTo(-31)
                 }
             }else if itemModel.style == .textPicGenerate{
                 title = "Generate (\(kPurchaseDefault.freeNum(type: .textGeneratePic)))"
@@ -189,6 +176,7 @@ extension TSGenmojiGennerateCell: UITextViewDelegate{
     
     func textViewDidChange(_ textView: UITextView) {
         submitBtn.isEnabled = textView.text.replacingOccurrences(of: " ", with: "") .count > 0
+        clearBtn.isHidden = !submitBtn.isEnabled
     }
     
     // 实现 UITextViewDelegate 协议方法,控制 return 键行为

+ 38 - 6
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/ViewModel/TSGenmojiCollectionViewModel.swift

@@ -26,16 +26,28 @@ class TSGenmojiColViewModel: TSBaseModel {
 enum TSGenmojiCoLStyple : Int {
     case generate //创造Genmoji AI 图
     case history  //创造Genmoji 后的历史记录
+    
     case textPicGenerate  //创造文生图后 AI 图
     case textPicHistory  //创造文生图后的历史记录
+    
+    case ptpEntrance    //图生图入口
+    case ptpUpload      //图生图上传
+    case ptpSelectStyle //图生图选择样式
+    
+    
     var sectionInset: UIEdgeInsets {
         switch self {
+            
         case .generate,.textPicGenerate:
             return UIEdgeInsets(top: 17, left: 17, bottom: 20, right: 15)
         case .history:
             return UIEdgeInsets(top: 0, left: 24, bottom: 10, right: 24)
         case .textPicHistory:
             return UIEdgeInsets(top: 0, left: 16, bottom: 10, right: 16)
+        case .ptpEntrance:
+            return UIEdgeInsets(top: 17, left: 17, bottom: 0, right: 15)
+        case .ptpUpload,.ptpSelectStyle:
+            return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
         }
     }
         
@@ -47,6 +59,8 @@ enum TSGenmojiCoLStyple : Int {
             return 16
         case .textPicHistory:
             return 13
+        case .ptpEntrance,.ptpUpload,.ptpSelectStyle:
+            return 0
         }
     }
     
@@ -58,6 +72,8 @@ enum TSGenmojiCoLStyple : Int {
             return 16
         case .textPicHistory:
             return 13
+        case .ptpEntrance,.ptpUpload,.ptpSelectStyle:
+            return 0
         }
     }
 
@@ -65,11 +81,17 @@ enum TSGenmojiCoLStyple : Int {
     var cellSize:CGSize {
         switch self {
         case .generate,.textPicGenerate:
-            return CGSize(width: k_ScreenWidth-32, height: 303)
+            return CGSize(width: k_ScreenWidth-32, height: 316)
         case .history:
             return CGSize(width: 88, height: 88)
         case .textPicHistory:
             return CGSize(width: kTextPicHistoryW, height: kTextPicHistoryH)
+        case .ptpEntrance:
+            return CGSize(width: k_ScreenWidth-32, height: 117)
+        case .ptpUpload:
+            return CGSize(width: k_ScreenWidth, height: 248)
+        case .ptpSelectStyle:
+            return CGSize(width: k_ScreenWidth, height: 110)
         }
     }
     
@@ -79,14 +101,20 @@ enum TSGenmojiCoLStyple : Int {
             return TSGenmojiGennerateCell.self
         case .history,.textPicHistory:
             return TSGenmojiItemCell.self
+        case .ptpEntrance:
+            return TSPTPGeneratorCell.self
+        case .ptpUpload:
+            return TSPTPUploadCell.self
+        case .ptpSelectStyle:
+            return TSPTPSelectStyleCell.self
         }
     }
     
     var headerViewSize:CGSize {
         switch self {
-        case .generate,.textPicGenerate:
+        case .generate,.textPicGenerate,.ptpEntrance,.ptpUpload:
             return CGSize.zero
-        case .history,.textPicHistory:
+        case .history,.textPicHistory,.ptpSelectStyle:
             return CGSizeMake(k_ScreenWidth, 60)
         }
     }
@@ -142,11 +170,15 @@ extension TSGenmojiCoLSectionModel : TSCollectionViewSectionComponent{
 class TSGenmojiCoLItemModel: TSBaseModel {
     var style:TSGenmojiCoLStyple = .generate
     
+    // 主要是给历史记录使用的
     var dataModel:TSGenmojiModel = TSGenmojiModel()
-  
+    //给图生图选择样式使用
+    var ptpStyleModels:[TSPTPStyleModel] = []
+    
     override func mapping(map: ObjectMapper.Map) {
-        dataModel    <- map["dataModel"]
-        style    <- map["style"]
+        dataModel           <- map["dataModel"]
+        ptpStyleModels       <- map["ptpStyleModels"]
+        style               <- map["style"]
     }
 }
 

+ 259 - 0
AIEmoji/Business/TSPTPGeneratorVC/TSPTPBrowseVC/TSPTPBrowseVC.swift

@@ -0,0 +1,259 @@
+//
+//  TSPTPBrowseVC.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/26.
+//
+
+private let cellId = "TSPTPBrowseCell"
+class TSPTPBrowseVC: TSBottomAlertVC {
+
+    
+    var dataModelArray = [TSGenmojiModel]()
+    var currentImage:UIImage?{
+        let cell = collectionView.cellForItem(at: IndexPath(item: currentIndex, section: 0)) as? TSPTPBrowseCell
+        var image = cell?.netWorkImageView.image
+        image = image?.pngImage
+        return image
+    }
+
+    var currentModel:TSGenmojiModel?{
+        if let model = dataModelArray.safeObj(At: currentIndex){
+            return model
+        }
+        return nil
+    }
+    
+    
+    var currentIndex:Int = 0 {
+        didSet{
+            reloadUI()
+        }
+    }
+    
+    lazy var collectionView: UICollectionView = {
+
+        let layout = UICollectionViewFlowLayout()
+        layout.scrollDirection = .vertical
+        
+        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.showsVerticalScrollIndicator = false
+        collectionView.showsHorizontalScrollIndicator = false
+        collectionView.backgroundColor = .clear
+        if #available(iOS 11.0, *) {
+            collectionView.contentInsetAdjustmentBehavior = .never
+        }
+        collectionView.register(TSPTPBrowseCell.self, forCellWithReuseIdentifier: cellId)
+        collectionView.isPagingEnabled = true
+        collectionView.isHidden = true
+        if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
+            flowLayout.minimumInteritemSpacing = 0
+            flowLayout.minimumLineSpacing = 0
+            flowLayout.scrollDirection = .horizontal
+            flowLayout.itemSize = CGSize(width: k_ScreenWidth, height: 490*kDesignScale)
+        }
+        return collectionView
+    }()
+    
+    
+    lazy var leftBtn: UIButton = {
+        let leftBtn = UIButton.createButton(image: UIImage(named: "page_left")){ [weak self]  in
+            guard let self = self else { return }
+            self.collectionView.scrollToItem(at:  IndexPath(item: self.currentIndex-1, section: 0), at: .left, animated: true)
+        }
+        return leftBtn
+    }()
+    
+    lazy var rightBtn: UIButton = {
+        let rightBtn = UIButton.createButton(image: UIImage(named: "page_right")){ [weak self]  in
+            guard let self = self else { return }
+            self.collectionView.scrollToItem(at: IndexPath(item: self.currentIndex+1, section: 0), at: .left, animated: true)
+        }
+        return rightBtn
+    }()
+        
+    override func createView() {
+        super.createView()
+        
+        submitBtn.isHidden = false
+        submitBtn.setTitle("Save".localized, for: .normal)
+        cancelBtn.isHidden = true
+        confirmBtn.isHidden = true
+        
+        bottomView.frame = CGRectMake(0, 92, k_ScreenWidth, k_ScreenHeight-92)
+
+        bottomView.addSubview(collectionView)
+        collectionView.snp.makeConstraints { make in
+            make.leading.trailing.equalTo(0)
+            make.top.equalTo(36)
+            make.bottom.equalTo(confirmBtn.snp.top).offset(-30)
+        }
+        
+        bottomView.addSubview(leftBtn)
+        leftBtn.snp.makeConstraints { make in
+            make.leading.equalTo(17)
+            make.top.equalTo(244)
+            make.width.height.equalTo(40)
+        }
+        
+        bottomView.addSubview(rightBtn)
+        rightBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-18)
+            make.top.equalTo(244)
+            make.width.height.equalTo(40)
+        }
+        
+        kDelayMainShort {
+            self.collectionView.isHidden = false
+            self.collectionView.reloadData()
+            self.collectionView.setContentOffset(CGPoint(x: CGFloat(self.currentIndex) * self.collectionView.frame.size.width, y: 0), animated: false)
+
+            if let flowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
+                flowLayout.itemSize = self.collectionView.bounds.size
+            }
+        }
+    }
+    
+    @objc override func clickSubmitBtn(){
+        
+        if JudgeVip(){
+            return
+        }
+        
+        if let image = currentImage{
+            PhotoManagerShared.saveImageToAlbum(image) { success, error in
+                if success {
+                    kSavePhotoSuccesswShared.show(atView: self.view)
+                }else{
+                    debugPrint(error)
+                }
+            }
+        }else{
+            kShowToastDataMissing()
+        }
+    }
+    
+    
+    func JudgeVip() -> Bool {
+        return kJudgeVip(externalBool: currentModel?.response.vip ?? false , vc: self, closePageBlock: nil)
+    }
+}
+
+
+extension TSPTPBrowseVC {
+    
+    func reloadUI() {
+        //判断前后是否还有,以此来确定左右翻页按钮
+        var isHiddenLeft = false
+        var isHiddenRight = false
+        if dataModelArray.count == 1{
+            isHiddenLeft = true
+            isHiddenRight = true
+        }else if currentIndex == 0 {
+            isHiddenLeft = true
+        }else if currentIndex+1 >= dataModelArray.count {
+            isHiddenRight = true
+        }
+        
+        leftBtn.isHidden = isHiddenLeft
+        rightBtn.isHidden = isHiddenRight
+    }
+}
+
+//MARK: UICollectionViewDataSource
+extension TSPTPBrowseVC:UICollectionViewDataSource,UICollectionViewDelegate {
+    
+    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
+        resetIndexWithOffset(scrollView)
+    }
+
+    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
+        resetIndexWithOffset(scrollView)
+    }
+
+    private func resetIndexWithOffset(_ scrollView: UIScrollView) {
+        let item = Int((scrollView.contentOffset.x / scrollView.bounds.width).rounded())
+        currentIndex = item
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return dataModelArray.count
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! TSPTPBrowseCell
+        if let model = dataModelArray.safeObj(At: indexPath.item){
+            cell.netWorkImageView.setAsyncImage(urlString: model.response.resultUrl,placeholder: kPlaceholderImage,contentMode: .scaleAspectFit)
+            cell.vipImageView.isHidden = !model.response.vip
+            cell.textLabel.text = model.request.promptSort
+        }
+        return cell
+    }
+    
+}
+
+
+class TSPTPBrowseCell : TSBaseCollectionCell{
+
+    lazy var netWorkImageView : UIImageView = {
+        let netWorkImageView = UIImageView.createImageView(imageName: "",corner: 24.0)
+        return netWorkImageView
+    }()
+
+    lazy var textLabel: UILabel = {
+        let textLabel = UILabel.createLabel(
+            font: .font(size: 12),
+            textColor: .themeColor,
+            numberOfLines: 0
+        )
+        return textLabel
+    }()
+    
+
+    lazy var vipImageView: UIImageView = {
+        let vipImageView = UIImageView.createImageView(imageName:"vip_side_icon")
+        vipImageView.contentMode = .scaleToFill
+        vipImageView.isHidden = true
+        return vipImageView
+    }()
+
+    override func creatUI() {
+        let w = k_ScreenWidth - 120
+        let h = w/kTextWHScale
+        bgContentView.addSubview(netWorkImageView)
+        netWorkImageView.snp.makeConstraints { make in
+            make.centerX.equalToSuperview()
+            make.top.equalTo(0)
+            make.width.equalTo(w)
+            make.height.equalTo(h)
+        }
+        
+        let labelBgView = UIView()
+        labelBgView.backgroundColor = "#333333".uiColor
+        labelBgView.cornerRadius = 16.0
+        labelBgView.addSubview(textLabel)
+        textLabel.snp.makeConstraints { make in
+            make.top.leading.equalTo(12)
+            make.trailing.bottom.equalTo(-12)
+        }
+        
+        bgContentView.addSubview(labelBgView)
+        labelBgView.snp.makeConstraints { make in
+            make.top.equalTo(netWorkImageView.snp.bottom).offset(20)
+            make.centerX.equalToSuperview()
+            make.leading.greaterThanOrEqualTo(16)
+            make.trailing.lessThanOrEqualTo(-16)
+            make.bottom.lessThanOrEqualTo(0)
+        }
+        
+        netWorkImageView.addSubview(vipImageView)
+        vipImageView.snp.makeConstraints { make in
+            make.width.height.equalTo(40)
+            make.top.equalTo(-5)
+            make.trailing.equalTo(5)
+        }
+        
+    }
+}

+ 236 - 0
AIEmoji/Business/TSPTPGeneratorVC/TSPTPGeneratorVC/TSPTPGeneratorVC.swift

@@ -0,0 +1,236 @@
+//
+//  TSPTPGeneratorVC.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/26.
+//
+
+class TSPTPGeneratorVC: TSBottomAlertVC {
+    
+    var imageModel:TSGenmojiModel?
+    var complete:((TSGenmojiModel)->Void)
+    
+    var prompt:String
+    var promptSort:String
+    var imageUrl:String
+    var upLoadImage:UIImage
+
+    init(prompt:String,promptSort:String,imageUrl:String,upLoadImage:UIImage,complete:@escaping ((TSGenmojiModel)->Void)) {
+        self.prompt = prompt
+        self.promptSort = promptSort
+        self.imageUrl = imageUrl
+        self.upLoadImage = upLoadImage
+        self.complete = complete
+        super.init()
+    }
+    
+    lazy var viewModel: TSPTPGeneratorVM = {
+        let viewModel:TSPTPGeneratorVM = TSPTPGeneratorVM(prompt: prompt,upLoadImage:upLoadImage)
+        return viewModel
+    }()
+
+    @MainActor required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    lazy var netWorkImageView : UIImageView = {
+        let netWorkImageView = UIImageView.createImageView(imageName: "",corner: 16.0)
+        netWorkImageView.isHidden = true
+        return netWorkImageView
+    }()
+    
+    
+    lazy var generateInView : TSCommonloadingView = {
+        let generateInView = TSCommonloadingView()
+        return generateInView
+    }()
+    
+    //大保存按钮
+    lazy var bigSaveBtn: UIButton = {
+        let bigSaveBtn = kCreateNormalSubmitBtn(title: "Save".localized) { [weak self]  in
+            guard let self = self else { return }
+            clickConfirmBtn()
+        }
+        bigSaveBtn.isHidden = true
+        return bigSaveBtn
+    }()
+    
+    lazy var regenerateBtn: UIButton = {
+        let regenerateBtn = UIButton.createButton(
+            title: "regenerate",
+            image: UIImage(named: "refresh_gary"),
+            backgroundColor:.white.withAlphaComponent(0.1),
+            font: .font(size: 12),
+            titleColor: .white.withAlphaComponent(0.6),
+            corner: 8.0)
+        { [weak self]  in
+            guard let self = self else { return }
+            clickSubmitBtn()
+        }
+        regenerateBtn.contentEdgeInsets = UIEdgeInsets(top: 4, left: 7, bottom: 4, right: 7)
+        regenerateBtn.imageEdgeInsets = UIEdgeInsets(top: 0, left: -4, bottom: 0, right: 0)
+        regenerateBtn.isHidden = true
+        return regenerateBtn
+    }()
+    
+    override func createView() {
+        super.createView()
+        
+        bottomView.frame = CGRectMake(0, 92, k_ScreenWidth, k_ScreenHeight-92)
+        cancelBtn.isHidden = true
+        confirmBtn.isHidden = true
+        submitBtn.isHidden = true
+        
+        bottomView.addSubview(bigSaveBtn)
+        bigSaveBtn.snp.makeConstraints { make in
+            make.bottom.equalTo(-34)
+            make.centerX.equalToSuperview()
+            make.width.equalTo(329)
+            make.height.equalTo(60)
+        }
+        
+        bottomView.addSubview(generateInView)
+        generateInView.snp.makeConstraints { make in
+            make.center.equalToSuperview()
+        }
+        
+        bottomView.addSubview(netWorkImageView)
+        let netWorkImageViewW = k_ScreenWidth - 120.0
+        let netWorkImageViewH = netWorkImageViewW/kTextWHScale
+        netWorkImageView.snp.makeConstraints { make in
+            make.top.equalTo(90*kDesignScale)
+            make.centerX.equalToSuperview()
+            make.width.equalTo(netWorkImageViewW)
+            make.height.equalTo(netWorkImageViewH)
+        }
+
+        bottomView.addSubview(regenerateBtn)
+        regenerateBtn.snp.makeConstraints { make in
+            make.top.equalTo(netWorkImageView.snp.bottom).offset(16)
+            make.centerX.equalToSuperview()
+            make.height.equalTo(28)
+        }
+        
+    }
+    
+    override func closePage() {
+        viewModel.cancelAllRequest()
+        self.dismiss(animated: true, completion: nil)
+    }
+    
+    //重试
+    @objc override func clickSubmitBtn(){
+        //判断 vip
+        if kJudgeVip(externalBool: kPurchaseDefault.freeNumAvailable(type: .picToPic) == false, vc: self) {[weak self] in
+            guard let self = self else { return }
+        }{ return }
+        
+        viewModel.uploadAndCreatImage()
+    }
+    
+    //保存功能
+    @objc override func clickConfirmBtn(){
+        if let image = getSuccessImage() {
+            PhotoManagerShared.saveImageToAlbum(image) { success, error in
+                if success {
+                    kSavePhotoSuccesswShared.show(atView:self.view)
+                }else{
+                    debugPrint(error)
+                }
+            }
+        }
+    }
+    
+    override func dealThings() {
+        viewModel.uploadAndCreatImage()
+        viewModel.$stateDatauPblished.receive(on: DispatchQueue.main).sink {[weak self]  (state,model) in
+            guard let self = self else { return }
+            self.upDateView(state: state, model: model)
+        }.store(in: &cancellable)
+    }
+    
+}
+extension TSPTPGeneratorVC {
+    
+    
+    func getSuccessImage()->UIImage?{
+        if let image = netWorkImageView.image {
+            return image.pngImage
+        }
+        return nil
+    }
+    
+}
+extension TSPTPGeneratorVC {
+    func upDateView(state:TSProgressState,model:TSGenmojiModel?){
+        switch state {
+            case .failed(let errorStr):
+            showError(text: errorStr)
+            case .success:
+                if let model = model {
+                    showSuccess(model: model)
+                }else{
+                    showError(text: nil)
+                }
+            default:
+                showLoading()
+        }
+    }
+    
+    func showLoading(){
+        generateInView.isHidden = false
+        generateInView.showLoading(text: "Generating...".localized)
+        generateInView.isRotating = true
+        submitBtn.isHidden = true
+        confirmBtn.isHidden = true
+        bigSaveBtn.isHidden = true
+        isClickTheBlankClosePage = false
+        regenerateBtn.isHidden = true
+        netWorkImageView.isHidden = true
+    }
+    
+    func showError(text:String?){
+        let msg = "Failed to Generate, please try later".localized
+        generateInView.isHidden = false
+        generateInView.showError(text: msg)
+        generateInView.isRotating = false
+        submitBtn.isHidden = false
+        bigSaveBtn.isHidden = true
+        isClickTheBlankClosePage = true
+        regenerateBtn.isHidden = true
+        netWorkImageView.isHidden = true
+    }
+    
+    func showSuccess(model:TSGenmojiModel){
+        generateInView.isHidden = true
+        generateInView.isRotating = false
+        submitBtn.isHidden = true
+        bigSaveBtn.isHidden = false
+        imageModel = model
+        isClickTheBlankClosePage = true
+        regenerateBtn.isHidden = false
+        
+        cancelBtn.isEnabled = false
+        confirmBtn.isEnabled = false
+        netWorkImageView.isHidden = false
+        
+//        netWorkImageView.snp.updateConstraints { make in
+//            make.top.equalTo(94)
+//            make.centerX.equalToSuperview()
+//            make.width.equalTo(249*kDesignScale)
+//            make.height.equalTo(441*kDesignScale)
+//        }
+        
+        self.netWorkImageView.setAsyncImage(urlString: model.response.resultUrl,placeholder:kPlaceholderImage){ [weak self] image in
+            guard let self = self else { return }
+            cancelBtn.isEnabled = true
+            confirmBtn.isEnabled = true
+        }
+        
+        kPurchaseDefault.useOnceForFree(type: .picToPic)
+        if let model = imageModel {
+            model.request.promptSort = promptSort
+            complete(model)
+        }
+    }
+}

+ 146 - 0
AIEmoji/Business/TSPTPGeneratorVC/TSPTPGeneratorVC/VM/TSPTPGeneratorVM.swift

@@ -0,0 +1,146 @@
+//
+//  TSPTPGeneratorVM.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/25.
+//
+import Alamofire
+class TSPTPGeneratorVM {
+    
+    var uploadRequest:Request?
+    var creatRequest:Request?
+    var queryRequest:Request?
+    var stopNetwork = false
+    
+    @Published var stateDatauPblished:(TSProgressState,TSGenmojiModel?) = (TSProgressState.none,nil)
+   
+    var prompt:String
+    var imageUrl:String?
+    var upLoadImage:UIImage
+    
+    init(prompt:String,upLoadImage:UIImage) {
+        self.prompt = prompt
+        self.upLoadImage = upLoadImage
+    }
+    
+
+    func creatImage() {
+        
+        guard let imageUrl = imageUrl else { return }
+        
+        stopNetwork = false
+        stateDatauPblished = (.start,nil)
+        creatRequest = TSNetworkShared.post(urlType: .imageRewrite,parameters: ["prompt":prompt,"imageUrl":imageUrl]) { [weak self] data,error in
+            guard let self = self else { return }
+            
+            if let dataDict = data as? [String:Any] ,
+               dataDict.safeInt(forKey: "code") == 200,
+               let actionId = dataDict["actionId"] as? Int{
+                if stopNetwork == false {
+                    self.getActionInfo(action_id:actionId)
+                }
+            }else{
+                self.stateDatauPblished = (.failed(error?.localizedDescription ?? ""),nil)
+            }
+        }
+    }
+    
+    func getActionInfo(action_id:Int){
+        queryRequest = TSNetworkShared.get(urlType: .actionInfo,parameters: ["action_id":action_id]) { [weak self] data,error in
+            guard let self = self else { return }
+            if let result = kNetWorkResultSuccess(data: data) {
+                if let genmojiModel = TSGenmojiModel(JSON: result) {
+                    switch genmojiModel.actionStatus {
+                    case .success:
+                        TSToastShared.hideLoading()
+                        self.stateDatauPblished = (.success(nil),genmojiModel)
+                    case .failed:
+                        self.stateDatauPblished = (.failed(kNetWorkMessage(data: data) ?? ""),nil)
+                    default:
+                        if stopNetwork == false {
+                            kDelayOnMainThread(1.0) {
+                                self.getActionInfo(action_id: action_id)
+                            }
+                        }
+                    }
+                }
+            }else{
+                self.stateDatauPblished = (.failed(error?.localizedDescription ?? ""),nil)
+            }
+        }
+    }
+    
+    
+    func cancelAllRequest(){
+        creatRequest?.cancel()
+        queryRequest?.cancel()
+        stopNetwork = true
+    }
+    
+    
+    func uploadAndCreatImage() {
+
+        if let imageUrl = imageUrl,imageUrl.contains("http") {
+            creatImage()
+            return
+        }
+        
+        stopNetwork = false
+        stateDatauPblished = (.start,nil)
+        uploadImage { progress in
+            
+        } completion: { [weak self]  data, error in
+            guard let self = self else { return }
+            if let error = error {
+                imageUrl = nil
+                self.stateDatauPblished = (.failed(error.localizedDescription),nil)
+            }else{
+                if let string = data as? String {
+                    imageUrl = string
+                    creatImage()
+                }
+            }
+        }
+    }
+}
+
+
+extension TSPTPGeneratorVM {
+    func uploadImage(
+        progressHandler: @escaping (Float) -> Void, // 上传进度回调
+        completion: @escaping (Any?, Error?) -> Void)
+    {
+        guard let imageData = upLoadImage.pngData() else { return }
+        stateDatauPblished = (.start,nil)
+        let dataArray = [
+            ["data": imageData,
+             "fieldName": "file", // 字段名
+             "fileName": "image.png", // 文件名
+             "mimeType": "image/png" // MIME 类型
+            ]
+        ]
+        
+        uploadRequest = TSNetworkShared.uploadData(
+            urlType: .upload,
+            dataArray: dataArray,
+            progressHandler: { progress in
+                progressHandler(progress)
+            },completion: { [weak self] result in
+            guard let self = self else { return }
+            switch result {
+            case .success(let data):
+                if let dataDict = data as? [String:Any] ,
+                   dataDict.safeInt(forKey: "code") == 200,
+                   let picUrl = dataDict["result"] as? String{
+                    completion(picUrl,nil)
+                }else{
+                    let error = NSError(domain: "Service exception", code: 0)
+                    completion(nil,error)
+                }
+            case .failure(let error):
+                completion(nil,error)
+            }
+        })
+    }
+    
+}

+ 28 - 0
AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/Cell/TSPTPGeneratorCell.swift

@@ -0,0 +1,28 @@
+//
+//  TSPTPGeneratorCell.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/25.
+//
+
+
+class TSPTPGeneratorCell : TSBaseCollectionCell{
+    
+    lazy var bannerBtn: UIButton = {
+        let bannerBtn = UIButton.createButton(backgroundImage: UIImage(named: "ptp_banner"))
+        bannerBtn.isUserInteractionEnabled = false
+//        { [weak self]  in
+//            guard let self = self else { return }
+//            actionHandler(any: "PTP")
+//        }
+        return bannerBtn
+    }()
+    
+    override func creatUI() {
+        contentView.addSubview(bannerBtn)
+        bannerBtn.snp.makeConstraints { make in
+            make.leading.bottom.trailing.top.equalTo(0)
+        }
+    }
+    
+}

+ 147 - 0
AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/Cell/TSPTPSelectStyleCell.swift

@@ -0,0 +1,147 @@
+//
+//  TSPTPSelectStyleCell.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/25.
+//
+
+class TSPTPSelectStyleCell : TSBaseCollectionCell{
+    var dataArray: [TSPTPStyleModel] = [TSPTPStyleModel](){
+        didSet{
+            styleCollectionView.reloadData()
+        }
+    }
+    
+    lazy var layout: UICollectionViewFlowLayout = {
+        let layout = UICollectionViewFlowLayout()
+        layout.scrollDirection = .horizontal
+        layout.itemSize = CGSize(width: 110, height: 110)
+        layout.minimumInteritemSpacing = 0.0
+        layout.minimumLineSpacing = 12.0
+        layout.sectionInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
+        return layout
+    }()
+    
+    
+    lazy var styleCollectionView: UICollectionView = {
+        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.showsVerticalScrollIndicator = false
+        collectionView.showsHorizontalScrollIndicator = false
+        collectionView.backgroundColor = .clear
+        collectionView.register(TSPTPSelectStyleCCell.self, forCellWithReuseIdentifier: TSPTPSelectStyleCCell.cellID)
+        if #available(iOS 11.0, *) {
+            collectionView.contentInsetAdjustmentBehavior = .never
+        }
+        return collectionView
+    }()
+
+    var currentIndexPath:IndexPath = IndexPath(item: 0, section: 0)
+    
+    override func creatUI() {
+        currentIndexPath = IndexPath(item: 0, section: 0)
+        addSubview(styleCollectionView)
+        styleCollectionView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+    }
+    
+    func unCheck(){
+        styleCollectionView.deselectItem(at: currentIndexPath, animated: true)
+    }
+    
+    override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
+        super.renderView(with: object, component: component, attributes: attributes)
+        if let itemModel = object as? TSGenmojiCoLItemModel{
+            dataArray = itemModel.ptpStyleModels
+            if self.dataArray.count > 0 {
+                kDelayMainShort{
+                    self.styleCollectionView.selectItem(at: self.currentIndexPath, animated: true, scrollPosition: .centeredHorizontally)
+                }
+            }
+        }
+    }
+}
+
+extension TSPTPSelectStyleCell: UICollectionViewDataSource ,UICollectionViewDelegate {
+    
+    public func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 1
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return dataArray.count
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TSPTPSelectStyleCCell.cellID, for: indexPath)
+        if let cell = cell as? TSPTPSelectStyleCCell,let itemModel = dataArray.safeObj(At: indexPath.item){
+            cell.itemModel = itemModel
+        }
+        return cell
+    }
+
+    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        if let model = dataArray.safeObj(At: indexPath.item){
+            currentIndexPath = indexPath
+            actionHandler(any: model)
+        }
+    }
+}
+
+class TSPTPSelectStyleCCell: TSBaseCollectionCell {
+    
+    static let cellID = "TSPTPSelectStyleCCell"
+
+    override var isSelected: Bool{
+        didSet{
+            boardImageView.isHidden = isSelected ? false : true
+        }
+    }
+    
+    var itemModel:TSPTPStyleModel = TSPTPStyleModel(){
+        didSet{
+            imageView.image = UIImage(named: itemModel.imageName)
+            textLabel.text = itemModel.imageText
+        }
+    }
+    
+    lazy var imageView: UIImageView = {
+        let imageView = UIImageView()
+        return imageView
+    }()
+    
+    lazy var boardImageView: UIImageView = {
+        let boardImageView = UIImageView.createImageView(imageName: "ptp_selected_border")
+        boardImageView.isHidden = true
+        return boardImageView
+    }()
+
+    lazy var textLabel: UILabel = {
+        let textLabel = UILabel.createLabel(font: .font(size: 14),textColor: .white,textAlignment: .center,numberOfLines: 0)
+        return textLabel
+    }()
+    
+    override func creatUI() {
+        
+        bgContentView.addSubview(imageView)
+        imageView.snp.makeConstraints { make in
+            make.top.bottom.leading.trailing.equalTo(0)
+        }
+        
+        bgContentView.addSubview(boardImageView)
+        boardImageView.snp.makeConstraints { make in
+            make.top.bottom.leading.trailing.equalTo(0)
+        }
+ 
+        bgContentView.addSubview(textLabel)
+        textLabel.snp.makeConstraints { make in
+            make.leading.trailing.equalTo(0)
+            make.bottom.equalTo(-4)
+            make.height.equalTo(37)
+        }
+    }
+    
+}

+ 125 - 0
AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/Cell/TSPTPUploadCell.swift

@@ -0,0 +1,125 @@
+//
+//  TSPTPUploadCell.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/25.
+//
+
+class TSPTPUploadCell : TSBaseCollectionCell{
+    
+    var upLoadImage:UIImage? = nil {
+        didSet{
+            if let image = upLoadImage {
+                upLoadView.isHidden = true
+                uploadImageView.isHidden = false
+                deleteBtn.isHidden = false
+                uploadImageView.image = image
+            }else {
+                upLoadView.isHidden = false
+                uploadImageView.isHidden = true
+                deleteBtn.isHidden = true
+                uploadImageView.image = nil
+            }
+        }
+    }
+    
+    lazy var upLoadView: UIView = {
+        let bgView = UIView()
+        
+        let addImageView = UIImageView.createImageView(imageName: "add")
+        bgView.addSubview(addImageView)
+        addImageView.snp.makeConstraints { make in
+            make.top.equalTo(0)
+            make.centerX.equalToSuperview()
+            make.width.height.equalTo(24)
+        }
+        
+        let textLabel = UILabel.createLabel(text: "Upload Image".localized,font: .font(size: 16),textColor: .white)
+        bgView.addSubview(textLabel)
+        textLabel.snp.makeConstraints { make in
+            make.top.equalTo(addImageView.snp.bottom).offset(16)
+            make.centerX.equalToSuperview()
+            make.bottom.equalToSuperview()
+        }
+        
+        return bgView
+    }()
+    
+    lazy var uploadImageView: UIImageView = {
+        let uploadImageView = UIImageView()
+        uploadImageView.contentMode = .scaleAspectFit
+        uploadImageView.cornerRadius = 12
+        upLoadView.isHidden = true
+        return uploadImageView
+    }()
+    
+    
+    lazy var bgView: UIView = {
+        
+        let bgView = UIView()
+        bgView.backgroundColor = .white.withAlphaComponent(0.1)
+        bgView.cornerRadius = 16
+        bgView.addGestureRecognizer(UITapGestureRecognizer(target: self, action:#selector(clickBgView)))
+        let bgImageView = UIImageView.createImageView(imageName: "ptp_upload_bg")
+        bgImageView.contentMode = .scaleToFill
+        bgView.addSubview(bgImageView)
+        bgImageView.snp.makeConstraints { make in
+            make.leading.bottom.trailing.top.equalTo(0)
+        }
+
+        bgView.addSubview(upLoadView)
+        upLoadView.snp.makeConstraints { make in
+            make.top.equalTo(88)
+            make.centerX.equalToSuperview()
+        }
+        
+        bgView.addSubview(uploadImageView)
+        uploadImageView.snp.makeConstraints { make in
+            make.top.leading.equalTo(8)
+            make.bottom.trailing.equalTo(-8)
+        }
+        
+        return bgView
+    }()
+    
+    
+    lazy var deleteBtn: UIButton = {
+        let deleteBtn = UIButton.createButton(backgroundImage: UIImage(named: "delete_redRound")) { [weak self]  in
+            guard let self = self else { return }
+            upLoadImage = nil
+            actionHandler(any: "delete")
+        }
+        deleteBtn.isHidden = true
+        return deleteBtn
+    }()
+    
+    override func creatUI() {
+        contentView.addSubview(bgView)
+        bgView.snp.makeConstraints { make in
+            make.leading.top.equalTo(16)
+            make.bottom.trailing.equalTo(-16)
+        }
+        
+        contentView.addSubview(deleteBtn)
+        deleteBtn.snp.makeConstraints { make in
+            make.top.equalTo(4)
+            make.trailing.equalTo(-4)
+            make.width.height.equalTo(32)
+        }
+    }
+    
+
+    @objc func clickBgView() {
+        actionHandler(any: "add")
+    }
+    
+    
+    override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
+        super.renderView(with: object, component: component, attributes: attributes)
+        if let itemModel = object as? TSGenmojiCoLItemModel{
+            if let model = itemModel.ptpStyleModels.first {
+                upLoadImage = model.image
+            }
+        }
+    }
+}

+ 24 - 0
AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/M/TSPTPStyleModel.swift

@@ -0,0 +1,24 @@
+//
+//  TSPTPStyleModel.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/25.
+//
+
+
+import ObjectMapper
+
+class TSPTPStyleModel: TSBaseModel {
+    var image:UIImage?//给上传图片用的
+    var imageName:String = ""
+    var imageText:String = ""
+    var prompt:String = ""
+    var isVip:Bool = false
+    override func mapping(map: ObjectMapper.Map) {
+        imageName               <- map["imageName"]
+        imageText               <- map["imageText"]
+        prompt                  <- map["prompt"]
+        isVip                   <- map["isVip"]
+    }
+}
+

+ 62 - 0
AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/M/photo_to_photo_style.json

@@ -0,0 +1,62 @@
+[
+    {
+        "imageName": "ptp_style_0",
+        "imageText": "Retro Anime",
+        "prompt":"retro anime cel-shading, 1980s Toei Animation style, bold black outlines, limited color palette (--color 1980s_anime), visible film grain --edge_threshold 0.4 --cel_shading 0.8, Retain the original stone size of the photo",
+        "isVip": false
+    },
+    {
+        "imageName": "ptp_style_1",
+        "imageText": "Cyberpunk",
+        "prompt":"cyberpunk cityscape with neon billboards, retro anime aesthetic like AKIRA, chromatic aberration effect, hand-painted gradient skies --edge_perspective 1.5 --glitch_overlay 0.3",
+        "isVip": false
+    },
+    {
+        "imageName": "ptp_style_2",
+        "imageText": "Retrofuturism",
+        "prompt":"Googie architecture space station, 1950s atomic age aesthetic, curved aluminum panels, starburst patterns, glass dome observatory --edge_threshold 0.5 --atomic_age_style 0.8 --chrome_reflection 1.2",
+        "isVip": false
+    },
+    {
+        "imageName": "ptp_style_3",
+        "imageText": "Vaporwave",
+        "prompt":"Digital sunset over databay, Windows 95 error pop-ups floating on waves, geometric islands with Doric columns --edge_fracture 0.7 --cyberliminal_space 1.2",
+        "isVip": false
+    },
+    {
+        "imageName": "ptp_style_4",
+        "imageText": "Pop Art",
+        "prompt":"Campbell soup can duplication | pantone 185C + process blue | benday dot overlay | consumerism critique | color_saturation=1.8, halftone_density=0.6",
+        "isVip": false
+    },
+    {
+        "imageName": "ptp_style_5",
+        "imageText": "Abstract Expressionism",
+        "prompt":"monochromatic tension fields | ivory black + lead white modulation | washi paper grain | zen emptiness | contrast_ratio=9:1, stroke_minimalism=true",
+        "isVip": false
+    },
+    {
+        "imageName": "ptp_style_6",
+        "imageText": "Classical Painting",
+        "prompt":"Rembrandt chiaroscuro lighting | velvet drapery folds | vermilion + burnt umber | Counter-Reformation pathos | brushstroke_variance=0.85, canvas_texture=0.7",
+        "isVip": false
+    },
+    {
+        "imageName": "ptp_style_7",
+        "imageText": "Fantasy Illustration",
+        "prompt":"bioluminescent flora | crystal architecture | twilight nymphs | celtic knot patterns | foliage_density=8, prism_refraction=0.75",
+        "isVip": false
+    },
+    {
+        "imageName": "ptp_style_8",
+        "imageText": "Photorealism",
+        "prompt":"liquid metal smartphone | cashmere fabric folds | perfume droplet collision | studio_hdr=9 | micro_scratch=0.03mm",
+        "isVip": false
+    },
+    {
+        "imageName": "ptp_style_9",
+        "imageText": "Modern Digital Art",
+        "prompt":"anodized_aluminum | optical_glass | monochrome_gradient | edge_sharpness=9 | ambient_occlusion=0.7",
+        "isVip": false
+    }
+]

+ 213 - 0
AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/TSPhotoToPhotoVC.swift

@@ -0,0 +1,213 @@
+//
+//  TSPhotoToPhotoVC.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/25.
+//
+
+class TSPhotoToPhotoVC: TSBaseVC {
+    lazy var viewModel:TSPhotoToPhotoVM = {
+        let vm = TSPhotoToPhotoVM()
+        vm.isCanGennerateBlock = { [weak self] isCan in
+            guard let self = self else { return }
+            submitBtn.isEnabled = isCan
+        }
+        return vm
+    }()
+    
+    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: k_Height_TabBar, right: 0)
+        
+        //点击 cell中的某个按钮
+        cp.itemActionHandler = { [weak self] cellCp, indexPath in
+            guard let self = self else { return }
+            if indexPath == IndexPath(item: 0, section: 0) {
+
+                if let text = cellCp as? String{
+                    if text == "add" {//选择图片
+                        photoPickerManager.pickSinglePhoto { [weak self] image in
+                            guard let self = self else { return }
+                            if let image = image {
+                                viewModel.upLoadImage = image
+                                cp.collectionView.reloadSections(IndexSet(integer: 0))
+//                                viewModel.uploadImage()
+                            }
+                        }
+                    }else if text == "delete"{//删除图片
+                        viewModel.upLoadImage = nil
+                        cp.collectionView.reloadSections(IndexSet(integer: 0))
+                    }
+                }
+            }else if let model = cellCp as? TSPTPStyleModel ,indexPath == IndexPath(item: 0, section: 1) {
+                //选择了某个样式
+                viewModel.selectedPTPStyleModel = model
+            }
+        }
+        
+        cp.sectionActionHandler = { [weak self] cellCp, indexPath in
+            guard let self = self else { return }
+            if let cmd = cellCp as? String, cmd == "delete"  {
+                showCustomAlert(message: "Are you sure to delete".localized, deleteHandler:  {
+                    self.viewModel.removeAllHistoryList()
+                    self.collectionComponent.clear()
+                    self.collectionComponent.reloadView(with: self.viewModel.colDataArray)
+                })
+            }
+        }
+        
+        cp.itemDidSelectedHandler = { [weak self] (object, indexPath) in
+            guard let self = self else { return }
+            if indexPath.section == 0{
+                return
+            }
+            
+            if let sections = viewModel.colDataArray.safeObj(At: indexPath.section) as? TSGenmojiCoLSectionModel{
+                var dataModelArray:[TSGenmojiModel] = []
+                for itemModel in sections.items {
+                    dataModelArray.append(itemModel.dataModel)
+                }
+                
+                let browseVC = TSPTPBrowseVC()
+                browseVC.dataModelArray = dataModelArray
+                browseVC.currentIndex = indexPath.item
+                kPresentModalVC(target: self, modelVC: browseVC,transitionStyle: .crossDissolve)
+            }
+
+
+        }
+
+        return cp
+    }()
+    
+    
+    lazy var submitBtn: UIButton = {
+        let submitBtn = kCreateNormalSubmitBtn(title: getVipText()) { [weak self]  in
+            guard let self = self else { return }
+            uploadImage()
+        }
+        submitBtn.cornerRadius = 24.0
+        submitBtn.isEnabled = false
+        return submitBtn
+    }()
+
+
+    lazy var photoPickerManager: TSPhotoPickerManager = {
+        let photoPickerManager = TSPhotoPickerManager(viewController: self)
+        return photoPickerManager
+    }()
+    
+    override func createView() {
+    
+        addNormalNavBarView()
+        setPageTitle("AI Photo Generator".localized)
+        
+        contentView.addSubview(collectionComponent.collectionView)
+        collectionComponent.collectionView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        
+        collectionComponent.clear()
+        collectionComponent.reloadView(with:viewModel.colDataArray)
+        
+//        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(clickCollectionView))
+//        tapGesture.cancelsTouchesInView = false // 确保不影响其他点击事件
+//        collectionComponent.collectionView.addGestureRecognizer(tapGesture)
+        
+        contentView.addSubview(submitBtn)
+        submitBtn.snp.makeConstraints { make in
+            make.bottom.equalTo(-k_Height_safeAreaInsetsBottom())
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-16)
+            make.height.equalTo(48)
+        }
+        
+    }
+    
+    override func dealThings() {
+        NotificationCenter.default.addObserver(self, selector: #selector(vipInfoChanged), name: .kPurchaseDidChanged, object: nil)
+    }
+    
+    @objc func vipInfoChanged() {
+        kExecuteOnMainThread {
+            //submitBtn
+            self.submitBtn.setTitle(self.getVipText(), for: .normal)
+        }
+    }
+    
+    @objc func clickCollectionView() {
+        view.endEditing(true)
+    }
+
+}
+
+extension TSPhotoToPhotoVC {
+
+    func uploadImage(){
+        
+        generateImage()
+        return
+        
+//        if let upLoadImageUrl = viewModel.upLoadImageUrl, upLoadImageUrl.contains("http"){
+//            generateImage()
+//            return
+//        }
+//        
+//        viewModel.uploadImage { [weak self] progress in
+//            guard let self = self else { return }
+//            let progressInt = Int(progress * 100)
+//            let string = "\(progressInt)%"
+//            TSToastShared.showProgress(progress: progress, status: string, containerView: self.view)
+//        } completion: { [weak self]  data, error in
+//            TSToastShared.hideLoading()
+//            guard let self = self else { return }
+//            if let error = error {
+//                TSToastShared.showToast(text: error.localizedDescription)
+//            }else{
+//                if let string = data as? String {
+//                    viewModel.upLoadImageUrl = string
+//                    generateImage()
+//                }
+//            }
+//        }
+    }
+    
+    func generateImage() {
+        
+        //判断 vip
+        if kJudgeVip(externalBool: kPurchaseDefault.freeNumAvailable(type: .picToPic) == false, vc: self) { [weak self] in
+            guard let self = self else { return }
+        }{ return }
+        
+        guard let prompt = viewModel.selectedPTPStyleModel?.prompt else { return }
+        guard let imageText = viewModel.selectedPTPStyleModel?.imageText else { return }
+//        guard let imageUrl = viewModel.upLoadImageUrl else { return }
+//        guard imageUrl.contains("http") else { return }
+        guard let upLoadImage = viewModel.upLoadImage else { return }
+//        imageUrl = "https://be-aigc.oss-cn-shanghai.aliyuncs.com/f7e22b4e-45dc-41d7-91d4-4876c771f189.png"
+        
+        let gennerateVC = TSPTPGeneratorVC(prompt: prompt,promptSort: imageText , imageUrl: "",upLoadImage: upLoadImage) { [weak self] model in
+            guard let self = self else { return }
+            if viewModel.saveModel(model:model) {
+                collectionComponent.clear()
+                collectionComponent.reloadView(with:viewModel.colDataArray)
+            }else{
+                collectionComponent.reloadData()
+            }
+            
+            vipInfoChanged()
+        }
+        
+        kPresentModalVC(target: self, modelVC: gennerateVC,transitionStyle: .crossDissolve)
+    }
+    
+    
+    func getVipText()->String{
+        if kPurchaseDefault.isVip {
+            return "Generate"
+        }
+        return "Generate (\(kPurchaseDefault.freeNum(type: .picToPic)))"
+    }
+}
+

+ 223 - 0
AIEmoji/Business/TSPTPGeneratorVC/TSPhotoToPhotoVC/VM/TSPhotoToPhotoVM.swift

@@ -0,0 +1,223 @@
+//
+//  TSPTPStyleVM.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/25.
+//
+import Alamofire
+import ObjectMapper
+class TSPhotoToPhotoVM {
+    
+    var colDataArray:[TSComponent] = [TSComponent]()
+    
+    var uploadRequest:Request?
+    @Published var stateDatauPblished:(TSProgressState,TSGenmojiModel?) = (TSProgressState.none,nil)
+   
+
+    //上传图片模型,用于上传图片参数的传递
+    private var updateImageModel: TSPTPStyleModel = {
+        var updateImageModel = TSPTPStyleModel()
+        return updateImageModel
+    }()
+    
+    var upLoadImageUrl:String?
+    var upLoadImage:UIImage?{
+        didSet{
+            updateImageModel.image = upLoadImage
+            upLoadImageUrl = nil
+            isCanGennerateBlock?(isCanGennerate)
+        }
+    }
+    
+    var selectedPTPStyleModel:TSPTPStyleModel?{
+        didSet{
+            isCanGennerateBlock?(isCanGennerate)
+        }
+    }
+    
+    //选择类型组
+    lazy var ptpStyleModels: [TSPTPStyleModel] = {
+        var ptpStyleModels = [TSPTPStyleModel]()
+        if let dataArray = Mapper<TSPTPStyleModel>().mapArray(JSONfile: "photo_to_photo_style.json"){
+            ptpStyleModels = dataArray
+            
+            if let model = dataArray.first {
+                selectedPTPStyleModel = model //加上默认的选择
+            }
+        }
+        
+        return ptpStyleModels
+    }()
+    //历史记录
+    @UserDefault(key: "photoToPhotoHistoryListString", defaultValue: "")
+    private var historyListString: String
+    
+    
+    lazy var listModelArray: [TSGenmojiModel] = {
+        if let listModelArray = Mapper<TSGenmojiModel>().mapArray(JSONString: historyListString){
+            return listModelArray
+        }
+        return []
+    }()
+    
+    lazy var uploadSectionModel: TSGenmojiCoLSectionModel = {
+        let uploadSectionModel = TSGenmojiCoLSectionModel()
+        uploadSectionModel.style = .ptpUpload
+        uploadSectionModel.name = ""
+        
+        let itemModel = TSGenmojiCoLItemModel()
+        itemModel.style = .ptpUpload
+        itemModel.ptpStyleModels = [updateImageModel]
+        
+        uploadSectionModel.items = [itemModel]
+        return uploadSectionModel
+    }()
+    
+    
+    lazy var styleSectionModel: TSGenmojiCoLSectionModel = {
+        let styleSectionModel = TSGenmojiCoLSectionModel()
+        styleSectionModel.style = .ptpSelectStyle
+        styleSectionModel.name = "Select Style".localized
+        
+        let itemModel = TSGenmojiCoLItemModel()
+        itemModel.style = .ptpSelectStyle
+        itemModel.ptpStyleModels = ptpStyleModels
+        styleSectionModel.items = [itemModel]
+        
+        return styleSectionModel
+    }()
+    
+    lazy var historySeciton: TSGenmojiCoLSectionModel = {
+        let sectionModel = TSGenmojiCoLSectionModel()
+        sectionModel.style = .textPicHistory
+        sectionModel.name = "History".localized
+        for model in listModelArray {
+            let itemModel = TSGenmojiCoLItemModel()
+            itemModel.style = sectionModel.style
+            itemModel.dataModel = model
+            sectionModel.items.append(itemModel)
+        }
+        return sectionModel
+    }()
+    
+
+    var isCanGennerate:Bool {
+        if upLoadImage != nil , selectedPTPStyleModel != nil {
+            return true
+        }
+        return false
+    }
+    
+    var isCanGennerateBlock:((Bool)->Void)?
+    init() {
+        combinedData()
+    }
+    
+    func combinedData(){
+        colDataArray.removeAll()
+        colDataArray.append(uploadSectionModel)
+        colDataArray.append(styleSectionModel)
+        
+        if historySeciton.items.count > 0 {
+            colDataArray.append(historySeciton)
+        }
+    }
+    
+}
+ 
+extension TSPhotoToPhotoVM {
+    //历史记录
+    var modelArray:[TSGenmojiModel]{
+        get{
+            let sectionModel = TSGenmojiCoLSectionModel()
+            sectionModel.style = .textPicHistory
+            sectionModel.name = "History".localized
+        
+            if let modelArray = Mapper<TSGenmojiModel>().mapArray(JSONString: historyListString){
+                return modelArray
+            }
+            return []
+        }
+        set{
+            if let jsonString = newValue.toJSONString() {
+                historyListString = jsonString
+            }
+        }
+    }
+    
+    //返回值,是否需要清空后刷新
+    func saveModel(model:TSGenmojiModel)->Bool{
+        listModelArray.insert(model, at: 0)
+        if let jsonString = listModelArray.toJSONString() {
+            historyListString = jsonString
+        }
+        var isNeed = false
+        if historySeciton.items.count == 0 {
+            colDataArray.append(historySeciton)
+            isNeed = true
+        }
+          
+        let colItemModel = TSGenmojiCoLItemModel()
+        colItemModel.style = .textPicHistory
+        colItemModel.dataModel = model
+        historySeciton.items.insert(colItemModel, at: 0)
+        return isNeed
+    }
+    
+    
+    
+    func removeAllHistoryList(){
+        listModelArray.removeAll()
+        if let jsonString = listModelArray.toJSONString() {
+            historyListString = jsonString
+        }
+        
+        historySeciton.items.removeAll()
+        colDataArray.removeLast()
+    }
+    
+}
+
+
+
+
+extension TSPhotoToPhotoVM {
+    func uploadImage(
+        progressHandler: @escaping (Float) -> Void, // 上传进度回调
+        completion: @escaping (Any?, Error?) -> Void)
+    {
+        guard let image = upLoadImage else { return }
+        guard let imageData = image.pngData() else { return }
+        stateDatauPblished = (.start,nil)
+        let dataArray = [
+            ["data": imageData,
+             "fieldName": "file", // 字段名
+             "fileName": "image.png", // 文件名
+             "mimeType": "image/png" // MIME 类型
+            ]
+        ]
+        
+        uploadRequest = TSNetworkShared.uploadData(
+            urlType: .upload,
+            dataArray: dataArray,
+            progressHandler: { progress in
+                progressHandler(progress)
+            },completion: { [weak self] result in
+            guard let self = self else { return }
+            switch result {
+            case .success(let data):
+                if let dataDict = data as? [String:Any] ,
+                   dataDict.safeInt(forKey: "code") == 200,
+                   let picUrl = dataDict["result"] as? String{
+                    completion(picUrl,nil)
+                }else{
+                    let error = NSError(domain: "Service exception", code: 0)
+                    completion(nil,error)
+                }
+            case .failure(let error):
+                completion(nil,error)
+            }
+        })
+    }
+    
+}

+ 23 - 18
AIEmoji/Business/TSTextGeneralPictureVC/TSTextGeneralPictureVC/TSTextGeneralPictureVC.swift

@@ -57,7 +57,7 @@ class TSTextGeneralPictureVC: TSBaseVC {
         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) {
+           if let text = cellCp as? String ,indexPath == IndexPath(item: 0, section: 1) {
                 generateImage(text: text)
             }
         }
@@ -75,24 +75,26 @@ class TSTextGeneralPictureVC: TSBaseVC {
         
         cp.itemDidSelectedHandler = { [weak self] (object, indexPath) in
             guard let self = self else { return }
-            view.endEditing(true)
+
             if indexPath.section == 0{
-                return
-            }
-            
-            if let sections = viewModel.colDataArray.safeObj(At: indexPath.section) as? TSGenmojiCoLSectionModel{
-                var dataModelArray:[TSGenmojiModel] = []
-                for itemModel in sections.items {
-                    dataModelArray.append(itemModel.dataModel)
+                kPushVC(target: self, modelVC: TSPhotoToPhotoVC())
+
+            }else if indexPath.section == 1{
+   
+            }else if indexPath.section == 2{
+                if let sections = viewModel.colDataArray.safeObj(At: indexPath.section) as? TSGenmojiCoLSectionModel{
+                    var dataModelArray:[TSGenmojiModel] = []
+                    for itemModel in sections.items {
+                        dataModelArray.append(itemModel.dataModel)
+                    }
+                    
+                    let browseVC = TSBigIconBrowseVC()
+                    browseVC.dataModelArray = dataModelArray
+                    browseVC.currentIndex = indexPath.item
+                    kPresentModalVC(target: self, modelVC: browseVC,transitionStyle: .crossDissolve)
                 }
-                
-                let browseVC = TSBigIconBrowseVC()
-                browseVC.dataModelArray = dataModelArray
-                browseVC.currentIndex = indexPath.item
-                kPresentModalVC(target: self, modelVC: browseVC,transitionStyle: .crossDissolve)
             }
-
-
+            
         }
 
         return cp
@@ -110,10 +112,12 @@ class TSTextGeneralPictureVC: TSBaseVC {
             make.edges.equalToSuperview()
         }
         
-//        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(clickCollectionView)))
+        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(clickCollectionView))
+        tapGesture.cancelsTouchesInView = false // 确保不影响其他点击事件
+        collectionComponent.collectionView.addGestureRecognizer(tapGesture)
+
         collectionComponent.clear()
         collectionComponent.reloadView(with:viewModel.colDataArray)
-        
     }
     
     override func dealThings() {
@@ -153,4 +157,5 @@ extension TSTextGeneralPictureVC {
         
         kPresentModalVC(target: self, modelVC: gennerateVC,transitionStyle: .crossDissolve)
     }
+
 }

+ 16 - 0
AIEmoji/Business/TSTextGeneralPictureVC/TSTextGeneralPictureVC/ViewModel/TSTextGeneralPictureVM.swift

@@ -26,6 +26,20 @@ let kRandomTextArray:[String] = [
 class TSTextGeneralPictureVM {
     var colDataArray:[TSComponent] = [TSComponent]()
 
+    
+    lazy var ptpEntranceSectionModel: TSGenmojiCoLSectionModel = {
+        let ptpEntranceSectionModel = TSGenmojiCoLSectionModel()
+        ptpEntranceSectionModel.style = .ptpEntrance
+        ptpEntranceSectionModel.name = ""
+        
+        let itemModel = TSGenmojiCoLItemModel()
+        itemModel.style = .ptpEntrance
+        ptpEntranceSectionModel.items = [itemModel]
+        
+        return ptpEntranceSectionModel
+    }()
+    
+    
     lazy var generateSectionModel: TSGenmojiCoLSectionModel = {
         let generateSectionModel = TSGenmojiCoLSectionModel()
         generateSectionModel.style = .textPicGenerate
@@ -69,7 +83,9 @@ class TSTextGeneralPictureVM {
     
     func combinedData(){
         colDataArray.removeAll()
+        colDataArray.append(ptpEntranceSectionModel)
         colDataArray.append(generateSectionModel)
+        
         if enmojiHistorySeciton.items.count > 0 {
             colDataArray.append(enmojiHistorySeciton)
         }

+ 2 - 2
AIEmoji/Business/TSTextGeneralPictureVC/TSTextPicGennerateVC/TSTextPicGennerateVM.swift

@@ -14,8 +14,8 @@ let kTextWHScale = kTextPicW/kTextPicH
 
 class TSTextPicGennerateVM {
     
-    var creatRequest:DataRequest?
-    var queryRequest:DataRequest?
+    var creatRequest:Request?
+    var queryRequest:Request?
     var stopNetwork = false
    
     @Published var stateDatauPblished:(TSProgressState,TSGenmojiModel?) = (TSProgressState.none,nil)

+ 67 - 14
AIEmoji/Business/VIewTool/TSCommonloadingView.swift

@@ -5,15 +5,33 @@
 //  Created by 100Years on 2025/1/17.
 //
 
-
+import Kingfisher
 
 class TSCommonloadingView: TSBaseView {
     
     lazy var imageView: UIImageView = {
-        let imageView = UIImageView.createImageView(imageName: "image")
+        let imageView = UIImageView.createImageView(imageName: "failed")
+        imageView.isHidden = true
         return imageView
     }()
     
+    lazy var animatedImageView: AnimatedImageView = {
+        let animatedImageView = AnimatedImageView()
+        animatedImageView.autoPlayAnimatedImage = false
+        if let gifURL = Bundle.main.url(forResource: "rotatingAnimation", withExtension: "gif") {
+            animatedImageView.kf.setImage(with: gifURL, options: [.cacheOriginalImage]) { result in
+                switch result {
+                case .success(let value):
+                    print("GIF 加载成功: \(value.source.url?.absoluteString ?? "")")
+                case .failure(let error):
+                    print("GIF 加载失败: \(error.localizedDescription)")
+                }
+            }
+        }
+        
+        return animatedImageView
+    }()
+    
     lazy var textLabel: UILabel = {
         let textLabel = UILabel.createLabel(font: .font(size: 12),textColor: .fromHex("#FFFFFF",alpha: 0.6))
         return textLabel
@@ -24,11 +42,18 @@ class TSCommonloadingView: TSBaseView {
     override func creatUI() {
         contentView.addSubview(imageView)
         imageView.snp.makeConstraints { make in
-            make.width.height.equalTo(120.0)
+            make.width.height.equalTo(200.0)
             make.centerX.equalToSuperview()
             make.top.equalToSuperview()
         }
         
+        contentView.addSubview(animatedImageView)
+        animatedImageView.snp.makeConstraints { make in
+            make.width.height.equalTo(200.0)
+            make.centerX.equalToSuperview()
+            make.top.equalToSuperview()
+        }
+  
         contentView.addSubview(textLabel)
         textLabel.snp.makeConstraints { make in
             make.height.equalTo(18.0)
@@ -69,33 +94,61 @@ class TSCommonloadingView: TSBaseView {
     
     /// 开始旋转
     func startRotating(view:UIView,duration: Double = 2.0) {
-        targetView = view
-        stopRotating(view: view)
-        let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
-        rotationAnimation.toValue = CGFloat.pi * 2
-        rotationAnimation.duration = duration
-        rotationAnimation.isCumulative = true
-        rotationAnimation.repeatCount = .infinity
-
-        view.layer.add(rotationAnimation, forKey: "rotationAnimation")
+        animatedImageView.startAnimating()
     }
 
     /// 停止旋转
     func stopRotating(view:UIView) {
-        view.layer.removeAnimation(forKey: "rotationAnimation")
+        animatedImageView.stopAnimating()
     }
     
     func showLoading(text:String){
-        imageView.image = UIImage(named: "rotating")
+        animatedImageView.isHidden = false
+        imageView.isHidden = true
+        
         textLabel.text = text
         isRotating = true
     }
     
     func showError(text:String){
+        animatedImageView.isHidden = true
+        
+        imageView.isHidden = false
         imageView.image = UIImage(named: "failed")
+        
         textLabel.text = text
         isRotating = false
     }
+    
+//    /// 开始旋转
+//    func startRotating(view:UIView,duration: Double = 2.0) {
+//        targetView = view
+//        stopRotating(view: view)
+//        let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
+//        rotationAnimation.toValue = CGFloat.pi * 2
+//        rotationAnimation.duration = duration
+//        rotationAnimation.isCumulative = true
+//        rotationAnimation.repeatCount = .infinity
+//
+//        view.layer.add(rotationAnimation, forKey: "rotationAnimation")
+//    }
+//
+//    /// 停止旋转
+//    func stopRotating(view:UIView) {
+//        view.layer.removeAnimation(forKey: "rotationAnimation")
+//    }
+//    
+//    func showLoading(text:String){
+//        imageView.image = UIImage(named: "rotating")
+//        textLabel.text = text
+//        isRotating = true
+//    }
+//    
+//    func showError(text:String){
+//        imageView.image = UIImage(named: "failed")
+//        textLabel.text = text
+//        isRotating = false
+//    }
 
     
     

+ 31 - 8
AIEmoji/Common/NetworkManager/TSNetWork/TSNetWork+Business.swift

@@ -10,12 +10,12 @@ private let baseURL = "http://ai.100yearslater.com"
 import Alamofire
 enum TSNeURLType:String {
     
-    case imageEmoji = "/api/image/emoji"        //文生emoji
-    case actionInfo = "/api/action/info"        //查询生成过程接口
-    case chat = "/api/text/chat"                //AI 对话接口
+    case imageEmoji = "/api/image/emoji"         //文生emoji
+    case actionInfo = "/api/action/info"         //查询生成过程接口
+    case chat = "/api/text/chat"                 //AI 对话接口
     case textPicCreate = "/api/image/create"     //文生图
-    
-    
+    case upload = "/api/upload"                  //上传图片
+    case imageRewrite = "/api/image/rewrite"     //图生图
     
     func getUrlString() -> String {
         return baseURL + self.rawValue
@@ -31,7 +31,7 @@ extension TSNetworkManager {
         parameters: [String: Any]? = nil,
         responseType: T.Type? = nil,
         completion: @escaping (Result<Any, Error>) -> Void
-    ) -> DataRequest  {
+    ) -> Request  {
         let urlString = urlType.getUrlString()
         return request(method: .get, urlString: urlString, parameters:parameters) { result in
             completion(result)
@@ -50,7 +50,7 @@ extension TSNetworkManager {
         parameters: [String: Any]? = nil,
         responseType: T.Type? = nil,
         completion: @escaping (Result<Any, Error>) -> Void
-    ) -> DataRequest {
+    ) -> Request {
         let urlString = urlType.getUrlString()
         return request(method: .post, urlString: urlString, parameters:parameters) { result in
             completion(result)
@@ -103,12 +103,35 @@ extension TSNetworkManager {
         responseType: T.Type? = nil,
         streamHandler:@escaping (String) -> Void,
         completion: @escaping (Result<Any, Error>) -> Void
-    ) -> DataStreamRequest?{
+    ) -> Request?{
         let urlString = urlType.getUrlString()
         let streamRequest = postStreamRequest(urlString: urlString, parameters: parameters, streamHandler: streamHandler, completion: completion)
         return streamRequest
     }
 
+    
+    /// 上传多个 Data 数据
+    /// - Parameters:
+    ///   - urlType: TSNeURLType
+    ///   - dataArray: Data 数组,每个元素是一个字典,包含 Data 和字段名
+    ///   - parameters: 其他参数(可选)
+    ///   - headers: 自定义请求头(可选)
+    ///   - completion: 完成回调,返回结果或错误
+    func uploadData<T: TSBaseModel>(
+        urlType: TSNeURLType,
+        dataArray: [[String: Any]], // Data 数组,每个元素包含 Data 和字段名
+        parameters: [String: Any]? = nil,
+        responseType: T.Type? = nil,
+        progressHandler: @escaping (Float) -> Void, // 上传进度回调
+        completion: @escaping (Result<Any, Error>) -> Void
+    ) -> Request?{
+        let urlString = urlType.getUrlString()
+        let request = uploadData(urlString: urlString,dataArray:dataArray, parameters: parameters, progressHandler: { progress in
+            progressHandler(Float(progress.fractionCompleted))
+        },completion: completion)
+        return request
+    }
+
 }
 
 

+ 2 - 2
AIEmoji/Common/NetworkManager/TSNetWork/TSNetworkManager+Loading.swift

@@ -14,7 +14,7 @@ extension TSNetworkManager {
         responseType: T.Type? = nil,
         animationView:UIView? = nil,
         completion: @escaping (Any?, Error?) -> Void
-    ) -> DataRequest {
+    ) -> Request {
         
         var isShowAnimation = false
         if animationView != nil {
@@ -55,7 +55,7 @@ extension TSNetworkManager {
         responseType: T.Type? = nil,
         animationView:UIView? = nil,
         completion: @escaping (Any?, Error?) -> Void
-    ) -> DataRequest {
+    ) -> Request {
         var isShowAnimation = false
         if animationView != nil {
             isShowAnimation = true

+ 116 - 3
AIEmoji/Common/NetworkManager/TSNetWork/TSNetworkManager.swift

@@ -26,13 +26,12 @@ class TSNetworkManager {
     }
     
     
-    
     func postStreamRequest(
         urlString: String,
         parameters: [String: Any]? = nil,
         streamHandler:@escaping (String) -> Void,
         completion: @escaping (Result<Any, Error>) -> Void
-    )-> DataStreamRequest? {
+    )-> Request? {
         
         guard let url = URL(string: urlString) else {
             completion(.failure(NSError(domain: "url nil", code: 0)))
@@ -85,7 +84,7 @@ class TSNetworkManager {
         urlString: String,
         parameters: [String: any Any & Sendable]? = nil,
         completion: @escaping (Result<Any, Error>) -> Void
-    ) -> DataRequest{
+    ) -> Request{
         dePrint("✈️✈️✈️网络请求:\(urlString)")
         dePrint("✈️✈️✈️参数:\(String(describing: parameters))")
         
@@ -100,6 +99,116 @@ class TSNetworkManager {
         return request
     }
     
+    
+
+    
+}
+
+extension TSNetworkManager {
+    
+    
+    /*
+     // Data 数组
+     let dataArray: [[String: Any]] = [
+         [
+             "data": imageData,
+             "fieldName": "file1", // 字段名
+             "fileName": "App-Icon.png", // 文件名
+             "mimeType": "image/png" // MIME 类型
+         ],
+         [
+             "data": textData,
+             "fieldName": "file2", // 字段名
+             "fileName": "hello.txt", // 文件名
+             "mimeType": "text/plain" // MIME 类型
+         ]
+     ]
+     */
+    
+    /// 上传多个 Data 数据
+    /// - Parameters:
+    ///   - url: 上传地址
+    ///   - dataArray: Data 数组,每个元素是一个字典,包含 Data 和字段名
+    ///   - parameters: 其他参数(可选)
+    ///   - headers: 自定义请求头(可选)
+    ///   - completion: 完成回调,返回结果或错误
+    func uploadData(
+        urlString: String,
+        dataArray: [[String: Any]], // Data 数组,每个元素包含 Data 和字段名
+        parameters: [String: Any]? = nil, // 其他参数
+        headers: HTTPHeaders? = nil, // 自定义请求头
+        progressHandler: @escaping (Progress) -> Void, // 上传进度回调
+        completion: @escaping (Result<Any, Error>) -> Void
+    )-> Request? {
+        
+        guard let url = URL(string: urlString) else {
+            completion(.failure(NSError(domain: "url nil", code: 0)))
+            return nil
+        }
+        
+        // 1. 设置默认请求头
+        var defaultHeaders: HTTPHeaders = [
+            "accept": "application/json",
+            "Content-Type": "multipart/form-data"
+        ]
+        // 合并自定义请求头
+        if let customHeaders = headers {
+            customHeaders.forEach { defaultHeaders[$0.name] = $0.value }
+        }
+        
+        dePrint("✈️✈️✈️网络请求:\(urlString)")
+        dePrint("✈️✈️✈️dataArray:\(String(describing: dataArray))")
+        dePrint("✈️✈️✈️参数:\(String(describing: parameters))")
+        // 2. 使用 Alamofire 上传 Data
+        let request = AF.upload(
+            multipartFormData: { multipartFormData in
+                // 添加 Data
+                for dataItem in dataArray {
+                    if let data = dataItem["data"] as? Data,
+                       let fieldName = dataItem["fieldName"] as? String,
+                       let fileName = dataItem["fileName"] as? String,
+                       let mimeType = dataItem["mimeType"] as? String {
+                        multipartFormData.append(
+                            data,
+                            withName: fieldName,
+                            fileName: fileName,
+                            mimeType: mimeType
+                        )
+                    }
+                }
+
+                // 添加其他参数
+                if let parameters = parameters {
+                    for (key, value) in parameters {
+                        if let data = "\(value)".data(using: .utf8) {
+                            multipartFormData.append(data, withName: key)
+                        }
+                    }
+                }
+            },
+            to: url, // 上传地址
+            headers: defaultHeaders // 请求头
+        )
+        
+        request
+            .uploadProgress{ progress in
+                // 3. 上传进度回调
+                // 上传进度回调
+                dePrint("✈️✈️✈️进度: \(progress.fractionCompleted * 100)%")
+                progressHandler(progress)
+            }
+            .responseString{ response in
+                // 4. 处理响应
+                self.handleResponse(response, completion: completion)
+        }
+        
+        return request
+    }
+
+}
+
+extension TSNetworkManager {
+    
     private func handleResponse(
         _ response: AFDataResponse<String>,
         completion: @escaping (Result<Any, Error>) -> Void
@@ -160,6 +269,10 @@ class TSNetworkManager {
     
 }
 
+
+
+
+
 func kNetWorkResultSuccess(data:Any?) -> [String:Any]? {
     guard let data = data else { return nil }
     if let dataDict = data as? [String:Any] ,

+ 5 - 2
AIEmoji/Common/Purchase/TSPurchaseManager.swift

@@ -20,6 +20,7 @@ public enum VipFreeNumType: String, CaseIterable {
     case generatePic            = "kGeneratePicFreeNum"
     case aichat                 = "kAIChatFreeNum"
     case textGeneratePic        = "kTextGeneratePicFreeNum"
+    case picToPic               = "kPicToPicFreeNum"
 }
 
 public struct PurchaseProduct {
@@ -509,7 +510,8 @@ extension PurchaseManager {
             completion(nil, NSError(domain: "Invalid URL", code: -1, userInfo: nil))
             return
         }
-        debugPrint("postRequest urlString=",urlString)
+        
+        dePrint("postRequest urlString=\(urlString)")
         // 创建请求
         var request = URLRequest(url: url)
         request.httpMethod = "POST"
@@ -580,7 +582,8 @@ extension PurchaseManager {
             freeDict = [
                 VipFreeNumType.generatePic.rawValue:3,
                 VipFreeNumType.aichat.rawValue:3,
-                VipFreeNumType.textGeneratePic.rawValue:3
+                VipFreeNumType.textGeneratePic.rawValue:3,
+                VipFreeNumType.picToPic.rawValue:3
             ]
             saveForFree()
         }

+ 135 - 0
AIEmoji/Common/View/TSPhotoPickerManager/TSPhotoPickerManager.swift

@@ -0,0 +1,135 @@
+//
+//  TSPhotoPickerManager.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/25.
+//
+
+import UIKit
+import PhotosUI
+
+class TSPhotoPickerManager: NSObject {
+    
+    // MARK: - Properties
+    private weak var viewController: UIViewController?
+    private var completionHandler: ((UIImage?) -> Void)?
+    
+    // MARK: - Initializers
+    init(viewController: UIViewController) {
+        self.viewController = viewController
+    }
+    
+    // MARK: - Public Methods
+    /// 打开照片选择器,单选一张照片
+    func pickSinglePhoto(completion: @escaping (UIImage?) -> Void) {
+        self.completionHandler = completion
+        
+        // 检查相册权限
+        checkPhotoLibraryPermission { [weak self] authorized in
+            guard let self = self else { return }
+            if authorized {
+                self.openPhotoPicker()
+            } else {
+                self.showPermissionAlert()
+            }
+        }
+    }
+    
+    // MARK: - Private Methods
+    /// 检查相册权限
+    private func checkPhotoLibraryPermission(completion: @escaping (Bool) -> Void) {
+        let status = PHPhotoLibrary.authorizationStatus()
+        switch status {
+        case .authorized:
+            completion(true)
+        case .notDetermined:
+            PHPhotoLibrary.requestAuthorization { newStatus in
+                DispatchQueue.main.async {
+                    completion(newStatus == .authorized)
+                }
+            }
+        default:
+            completion(false)
+        }
+    }
+    
+    /// 打开照片选择器
+    private func openPhotoPicker() {
+        if #available(iOS 14, *) {
+            var configuration = PHPickerConfiguration(photoLibrary: .shared())
+            configuration.selectionLimit = 1 // 只能选择一张照片
+            configuration.filter = .images // 只显示照片,不显示视频
+            configuration.preferredAssetRepresentationMode = .current // 使用高质量图片
+            
+            let picker = PHPickerViewController(configuration: configuration)
+            picker.delegate = self
+            viewController?.present(picker, animated: true, completion: nil)
+        } else {
+            // iOS 14 以下使用旧的 UIImagePickerController
+            let imagePicker = UIImagePickerController()
+            imagePicker.sourceType = .photoLibrary
+            imagePicker.delegate = self
+            imagePicker.mediaTypes = ["public.image"] // 只选择照片
+            viewController?.present(imagePicker, animated: true, completion: nil)
+        }
+    }
+    
+    /// 显示权限提示
+    private func showPermissionAlert() {
+        let alert = UIAlertController(
+            title: "相册权限未开启",
+            message: "请在设置中开启相册权限以选择照片",
+            preferredStyle: .alert
+        )
+        alert.addAction(UIAlertAction(title: "取消", style: .cancel, handler: nil))
+        alert.addAction(UIAlertAction(title: "去设置", style: .default) { _ in
+            if let url = URL(string: UIApplication.openSettingsURLString) {
+                UIApplication.shared.open(url, options: [:], completionHandler: nil)
+            }
+        })
+        viewController?.present(alert, animated: true, completion: nil)
+    }
+}
+
+// MARK: - PHPickerViewControllerDelegate (iOS 14+)
+@available(iOS 14, *)
+extension TSPhotoPickerManager: PHPickerViewControllerDelegate {
+    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
+        picker.dismiss(animated: true) {
+            guard let result = results.first else {
+                self.completionHandler?(nil)
+                return
+            }
+            
+            // 获取选中的图片
+            result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] (object, error) in
+                if let image = object as? UIImage {
+                    DispatchQueue.main.async {
+                        self?.completionHandler?(image)
+                    }
+                } else {
+                    DispatchQueue.main.async {
+                        self?.completionHandler?(nil)
+                    }
+                }
+            }
+        }
+    }
+}
+
+// MARK: - UIImagePickerControllerDelegate & UINavigationControllerDelegate (iOS 14 以下)
+extension TSPhotoPickerManager: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
+    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
+        picker.dismiss(animated: true) {
+            if let image = info[.originalImage] as? UIImage {
+                self.completionHandler?(image)
+            } else {
+                self.completionHandler?(nil)
+            }
+        }
+    }
+    
+    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
+        picker.dismiss(animated: true, completion: nil)
+    }
+}

BIN
AIEmoji/Res/rotatingAnimation.gif