Kaynağa Gözat

生成通许录海报和头像开发完毕

100Years 4 hafta önce
ebeveyn
işleme
54c6fb09eb
18 değiştirilmiş dosya ile 830 ekleme ve 64 silme
  1. 24 0
      AIRingtone.xcodeproj/project.pbxproj
  2. 22 0
      AIRingtone/Assets.xcassets/Common/ringPhotoBg.imageset/Contents.json
  3. BIN
      AIRingtone/Assets.xcassets/Common/ringPhotoBg.imageset/ringPhotoBg@2x.png
  4. BIN
      AIRingtone/Assets.xcassets/Common/ringPhotoBg.imageset/ringPhotoBg@3x.png
  5. 36 1
      AIRingtone/Business/Data/TSUserDefaultData.swift
  6. 19 0
      AIRingtone/Business/TSAIPhotoVC/TSAIPhotoChildVC/TSAIPhotoChildVC.swift
  7. 144 6
      AIRingtone/Business/TSAIPhotoVC/TSAIPhotoChildVC/View/TSAIPhotoImageCell.swift
  8. 12 9
      AIRingtone/Business/TSAIPhotoVC/TSGeneralPicVC/TSGeneralPicVC+Event.swift
  9. 45 24
      AIRingtone/Business/TSAIPhotoVC/TSGeneralPicVC/TSGeneralPicVC.swift
  10. 22 5
      AIRingtone/Business/TSAIPhotoVC/TSTextGeneralPicVC/TSTextGeneralPicVC.swift
  11. 11 7
      AIRingtone/Business/TSAIPhotoVC/TSTextGeneralPicVC/VM/TSTextGeneralPicVM.swift
  12. 5 5
      AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/View/TSAIRintoneHistoryCell.swift
  13. 0 1
      AIRingtone/Business/TSAIRintoneVC/TSGeneralRintoneVC/TSGeneralRintoneVC.swift
  14. 0 5
      AIRingtone/Business/TSAIRintoneVC/TSTextGeneralRintoneVC/TSTextGeneralRintoneVC.swift
  15. 4 0
      AIRingtone/Business/TSAIRintoneVC/TSTextGeneralRintoneVC/VM/TSTextGeneralRintoneVM.swift
  16. 8 1
      AIRingtone/Common/Ex/Notification+TSEx.swift
  17. 239 0
      AIRingtone/Common/Tool/OperationQueue/TSGeneratePhotoOperation/TSGeneratePhotoOperation.swift
  18. 239 0
      AIRingtone/Common/Tool/OperationQueue/TSGeneratePosterOperation/TSGeneratePosterOperation.swift

+ 24 - 0
AIRingtone.xcodeproj/project.pbxproj

@@ -56,6 +56,8 @@
 		A840A7F22D8E60A40044B8B9 /* ringAnimation.gif in Resources */ = {isa = PBXBuildFile; fileRef = A840A7F12D8E60A40044B8B9 /* ringAnimation.gif */; };
 		A840A7F42D8EA0340044B8B9 /* TSGeneralPhotoBrowseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A840A7F32D8EA02E0044B8B9 /* TSGeneralPhotoBrowseVC.swift */; };
 		A840A7F62D8EA0470044B8B9 /* TSGeneralPosterBrowseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A840A7F52D8EA0380044B8B9 /* TSGeneralPosterBrowseVC.swift */; };
+		A840A7FA2D916D9B0044B8B9 /* TSGeneratePosterOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A840A7F92D916D9A0044B8B9 /* TSGeneratePosterOperation.swift */; };
+		A840A7FE2D916DB50044B8B9 /* TSGeneratePhotoOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A840A7FD2D916DB40044B8B9 /* TSGeneratePhotoOperation.swift */; };
 		A868A89A2D75505E00F6D884 /* TSThemeBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868A8992D75505800F6D884 /* TSThemeBannerCell.swift */; };
 		A868A89C2D75506C00F6D884 /* TSThemeContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868A89B2D75506500F6D884 /* TSThemeContentCell.swift */; };
 		A868A8A22D7560B900F6D884 /* TSPageNullView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868A89D2D7560B900F6D884 /* TSPageNullView.swift */; };
@@ -193,6 +195,8 @@
 		A840A7F12D8E60A40044B8B9 /* ringAnimation.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = ringAnimation.gif; sourceTree = "<group>"; };
 		A840A7F32D8EA02E0044B8B9 /* TSGeneralPhotoBrowseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGeneralPhotoBrowseVC.swift; sourceTree = "<group>"; };
 		A840A7F52D8EA0380044B8B9 /* TSGeneralPosterBrowseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGeneralPosterBrowseVC.swift; sourceTree = "<group>"; };
+		A840A7F92D916D9A0044B8B9 /* TSGeneratePosterOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGeneratePosterOperation.swift; sourceTree = "<group>"; };
+		A840A7FD2D916DB40044B8B9 /* TSGeneratePhotoOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGeneratePhotoOperation.swift; sourceTree = "<group>"; };
 		A868A8992D75505800F6D884 /* TSThemeBannerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSThemeBannerCell.swift; sourceTree = "<group>"; };
 		A868A89B2D75506500F6D884 /* TSThemeContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSThemeContentCell.swift; sourceTree = "<group>"; };
 		A868A89D2D7560B900F6D884 /* TSPageNullView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPageNullView.swift; sourceTree = "<group>"; };
@@ -650,6 +654,22 @@
 			path = NotUse;
 			sourceTree = "<group>";
 		};
+		A840A7F72D916D7F0044B8B9 /* TSGeneratePhotoOperation */ = {
+			isa = PBXGroup;
+			children = (
+				A840A7FD2D916DB40044B8B9 /* TSGeneratePhotoOperation.swift */,
+			);
+			path = TSGeneratePhotoOperation;
+			sourceTree = "<group>";
+		};
+		A840A7F82D916D8E0044B8B9 /* TSGeneratePosterOperation */ = {
+			isa = PBXGroup;
+			children = (
+				A840A7F92D916D9A0044B8B9 /* TSGeneratePosterOperation.swift */,
+			);
+			path = TSGeneratePosterOperation;
+			sourceTree = "<group>";
+		};
 		A868A8982D75505100F6D884 /* View */ = {
 			isa = PBXGroup;
 			children = (
@@ -1034,6 +1054,8 @@
 		A8D776F62D8D342D007EAB35 /* OperationQueue */ = {
 			isa = PBXGroup;
 			children = (
+				A840A7F82D916D8E0044B8B9 /* TSGeneratePosterOperation */,
+				A840A7F72D916D7F0044B8B9 /* TSGeneratePhotoOperation */,
 				A840A7E92D8D81350044B8B9 /* TSGenerateRintoneOperation */,
 				A8D776F72D8D343F007EAB35 /* TSBaseOperationQueue.swift */,
 				A8D776F92D8D345B007EAB35 /* TSBaseOperation.swift */,
@@ -1269,11 +1291,13 @@
 				A83F87222D7953C000D29B1B /* Notification+TSEx.swift in Sources */,
 				A83503CD2D7ED1440067002A /* TSThemeTutorialsVC.swift in Sources */,
 				A899D3622D82D89C00AB9C1C /* TSButton.swift in Sources */,
+				A840A7FA2D916D9B0044B8B9 /* TSGeneratePosterOperation.swift in Sources */,
 				A868A8AD2D758B6B00F6D884 /* TSTBCallPreviewView.swift in Sources */,
 				A868A9162D784E6500F6D884 /* TSBottomAlertVC.swift in Sources */,
 				A80EDF012D718DF1003CD332 /* TSBusinessWebVC.swift in Sources */,
 				A868A8A72D757DD600F6D884 /* UICollectionView+Refresh.swift in Sources */,
 				A868A8F82D77E2BC00F6D884 /* TSTextGeneralPicVC.swift in Sources */,
+				A840A7FE2D916DB50044B8B9 /* TSGeneratePhotoOperation.swift in Sources */,
 				A80EDF022D718DF1003CD332 /* TSSettingListView.swift in Sources */,
 				A899D3CB2D89A53D00AB9C1C /* TSDownloadManager.swift in Sources */,
 				A8CC55902D799F9F002E0CAA /* TSAIRintoneVM.swift in Sources */,

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

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

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


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


+ 36 - 1
AIRingtone/Business/Data/TSUserDefaultData.swift

@@ -35,6 +35,24 @@ class TSPosterHistory{
         self.saveHistoryString()
     }
     
+    
+    static func replaceModel(oldID: Int, newModel: TSActionInfoModel) {
+        if let index = self.listModelArray.firstIndex(where: {$0.id == oldID}) {
+            self.listModelArray[index] = newModel
+            dePrint("TSPosterHistory.listModelArray Model replaced at index \(index)")
+        } else {
+            self.listModelArray.insert(newModel, at: 0)
+            dePrint("TSPosterHistory.listModel ArrayModel not found")
+        }
+        dePrint("TSPosterHistory.listModelArray.count=\(TSAIRintoneHistory.listModelArray.count)")
+        
+        if newModel.status == "success" ||
+            newModel.status == "failed" {
+            self.saveHistoryString()
+        }
+   
+    }
+    
     static func removeIndex(index:Int){
         self.listModelArray.remove(at: index)
         self.saveHistoryString()
@@ -100,6 +118,23 @@ class TSPhotoHistory{
         self.saveHistoryString()
     }
     
+    static func replaceModel(oldID: Int, newModel: TSActionInfoModel) {
+        if let index = self.listModelArray.firstIndex(where: {$0.id == oldID}) {
+            self.listModelArray[index] = newModel
+            dePrint("TSPhotoHistory.listModelArray Model replaced at index \(index)")
+        } else {
+            self.listModelArray.insert(newModel, at: 0)
+            dePrint("TSPhotoHistory.listModel ArrayModel not found")
+        }
+        dePrint("TSPhotoHistory.listModelArray.count=\(TSAIRintoneHistory.listModelArray.count)")
+        
+        if newModel.status == "success" ||
+            newModel.status == "failed" {
+            self.saveHistoryString()
+        }
+   
+    }
+    
     static func saveHistoryString(){
         if let jsonString = listModelArray.toJSONString() {
             self.historyString = jsonString
@@ -223,7 +258,7 @@ class TSAIRintoneHistory{
 }
 
 
-//AI铃声历史记录
+//我的下载铃声历史记录
 class TSMineRintoneHistory{
     @UserDefault(key: "kMineRintoneHistoryListString", defaultValue: "")
     static private var historyString: String

+ 19 - 0
AIRingtone/Business/TSAIPhotoVC/TSAIPhotoChildVC/TSAIPhotoChildVC.swift

@@ -37,6 +37,13 @@ class TSAIPhotoChildVC: TSBaseVC {
             guard let self = self else { return }
             
             if let sections = vm.colDataArray.safeObj(At: indexPath.section) as? TSColVVMSectionModel{
+                
+                if let itemModel = sections.items.safeObj(At: indexPath.item) ,
+                   let model = itemModel.dataModel as? TSActionInfoModel,
+                   model.actionStatus != .success{
+                    return
+                }
+
                 var dataModelArray:[TSActionInfoModel] = []
                 for itemModel in sections.items {
                     if let model = itemModel.dataModel as? TSActionInfoModel {
@@ -89,6 +96,18 @@ class TSAIPhotoChildVC: TSBaseVC {
                 }
             }
         }
+        
+        NotificationCenter.default.addObserver(forName: self.style == .poster ? .kGeneratePosterOperationChanged :  .kGeneratePhotoOperationChanged , object: nil, queue: nil) { notification in
+            if let userInfo = notification.userInfo as? [String: Any], let uuid = userInfo["uuid"] as? String,let state = userInfo["state"] as? TSProgressState {
+                dePrint("TSBaseOperation stateDatauPblished 收到 = \(state)")
+                switch state {
+                case .start, .success(_),.failed(_):
+                    self.vm.setRecentData()
+                    self.reloadView()
+                default:break
+                }
+            }
+        }
     }
 }
 

+ 144 - 6
AIRingtone/Business/TSAIPhotoVC/TSAIPhotoChildVC/View/TSAIPhotoImageCell.swift

@@ -7,6 +7,10 @@
 
 class TSAIPhotoImageCell: TSBaseCollectionCell {
     
+    
+    var model:TSActionInfoModel?
+    var style = TSColVVMStyple.posterHistory
+    
     lazy var showImageView: UIImageView = {
         let showImageView = UIImageView.createImageView(imageName:"",contentMode: .scaleAspectFill)
         showImageView.backgroundColor = .gray
@@ -32,7 +36,26 @@ class TSAIPhotoImageCell: TSBaseCollectionCell {
         return exampleView
     }()
     
+    lazy var generateView: TSImageGenerateView = {
+        let generateView = TSImageGenerateView()
+        generateView.isHidden = true
+        generateView.refreshHandel = { [weak self]  in
+            guard let self = self else { return }
+            guard let oldModel = model else { return }
+            
+            if style == .posterHistory {
+                TSGeneratePosterOperationQueue.shared.creatOperation(uuid: oldModel.uuid).creatPoster(oldModel: oldModel, prompt: oldModel.request.prompt, promptSort: oldModel.request.promptSort)
+            }else if style == .photoHistory {
+                TSGeneratePhotoOperationQueue.shared.creatOperation(uuid: oldModel.uuid).creatPhoto(oldModel: oldModel, prompt: oldModel.request.prompt, promptSort: oldModel.request.promptSort)
+            }
+            
+        }
+        return generateView
+    }()
+    
+    
     override func creatUI() {
+        contentView.clipsToBounds = true
         contentView.addSubview(showImageView)
         showImageView.snp.makeConstraints { make in
             make.top.equalTo(0)
@@ -41,9 +64,9 @@ class TSAIPhotoImageCell: TSBaseCollectionCell {
         }
         
         if self.bounds.size.width == self.bounds.size.height {
-            showImageView.layer.cornerRadius = self.bounds.size.width/2
+            contentView.layer.cornerRadius = self.bounds.size.width/2
         }else{
-            showImageView.layer.cornerRadius = 16
+            contentView.layer.cornerRadius = 16
         }
         
         contentView.addSubview(exampleView)
@@ -53,18 +76,133 @@ class TSAIPhotoImageCell: TSBaseCollectionCell {
             make.height.equalTo(20)
         }
         
+        contentView.addSubview(generateView)
+        generateView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
     }
     
     override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
         super.renderView(with: object, component: component, attributes: attributes)
         if let itemModel = object as? TSColVVMItemModel{
-            if let generalPicModel = itemModel.dataModel as? TSActionInfoModel{
-                showImageView.image = nil
-                showImageView.setAsyncImage(urlString: generalPicModel.response.resultUrl,placeholder: kPlaceholderImage,contentMode: .scaleAspectFill)
-                exampleView.isHidden = generalPicModel.modelType == .example ? false : true
+            if let model = itemModel.dataModel as? TSActionInfoModel{
+                self.model = model
+                style = itemModel.style
+                generateView.style = style
+                if itemModel.style == .posterHistory {
+                    if let rintoneOperation = TSGeneratePosterOperationQueue.shared.findOperation(uuid: model.uuid) as? TSGeneratePosterOperation {
+                        DispatchQueue.main.async {
+                            rintoneOperation.currentActionInfoModelChanged = { [weak self] actionInfoModel in
+                                guard let self = self else { return }
+                                DispatchQueue.main.async {
+                                    self.updataActionInfoModelView(model: actionInfoModel)
+                                }
+                            }
+                        }
+                    }
+                }else if itemModel.style == .photoHistory {
+                    if let rintoneOperation = TSGeneratePhotoOperationQueue.shared.findOperation(uuid: model.uuid) as? TSGeneratePhotoOperation {
+                        DispatchQueue.main.async {
+                            rintoneOperation.currentActionInfoModelChanged = { [weak self] actionInfoModel in
+                                guard let self = self else { return }
+                                DispatchQueue.main.async {
+                                    self.updataActionInfoModelView(model: actionInfoModel)
+                                }
+                            }
+                        }
+                    }
+                }
+                updataActionInfoModelView(model: model)
             }
         }
     }
     
+    func updataActionInfoModelView(model:TSActionInfoModel){
+        
+        if model.modelType == .example {
+            model.actionStatus = .success
+        }
+        dePrint("updataActionInfoModelView model.actionStatus 收到 = \(model.actionStatus)")
+        switch model.actionStatus {
+        case .pending,.running:
+            generateView.isHidden = false
+            generateView.setProgress(progress: model.percent)
+        case .success:
+            generateView.isHidden = true
+            
+            showImageView.setAsyncImage(urlString: model.response.resultUrl,placeholder: kPlaceholderImage,contentMode: .scaleAspectFill)
+            exampleView.isHidden = model.modelType == .example ? false : true
+            
+        case .failed:
+            generateView.isHidden = false
+            generateView.setFail()
+        }
+  
+    }
+    
 }
 
+class TSImageGenerateView:TSBaseView {
+    
+    var refreshHandel:(()->Void)?
+    var style = TSColVVMStyple.posterHistory
+    lazy var infoLabel: UILabel = {
+        let infoLabel = UILabel.createLabel(font: .font(size: 12),textColor: .white,textAlignment: .center,numberOfLines: 0)
+        return infoLabel
+    }()
+    
+    lazy var refreshBtn: TSUIExpandedTouchButton = {
+        let refreshBtn = TSUIExpandedTouchButton()
+        refreshBtn.setUpButton(image: UIImage(named: "refresh_white")){[weak self]  in
+            guard let self = self else { return }
+            refreshHandel?()
+        }
+        refreshBtn.isHidden = true
+        return refreshBtn
+    }()
+    
+    
+    override func creatUI() {
+        backgroundColor = .cardColor
+
+        
+        let bgImageView = UIImageView.createImageView(imageName: "ringPhotoBg",contentMode: .scaleAspectFill)
+        contentView.addSubview(bgImageView)
+        bgImageView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        
+        contentView.addSubview(infoLabel)
+        infoLabel.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()//.offset(10)
+            make.leading.equalTo(8)
+            make.trailing.equalTo(-8)
+        }
+        
+        contentView.addSubview(refreshBtn)
+        refreshBtn.snp.makeConstraints { make in
+            make.centerX.equalToSuperview()
+            make.top.equalTo(infoLabel.snp.bottom).offset(8)
+            make.width.height.equalTo(20.0)
+        }
+        
+    }
+    
+    func setProgress(progress:Float) {
+        refreshBtn.isHidden = true
+        let progressInt = Int(progress*100)
+        
+        if style == .posterHistory {
+            infoLabel.text = "Working on your contact poster\(progressInt)%..."
+        }else if style == .photoHistory {
+            infoLabel.text = "Working on your contact photo\(progressInt)%..."
+        }
+        
+    }
+    
+    func setFail(){
+        setProgress(progress: 0.0)
+        infoLabel.text = "Generation Failed".localized
+        refreshBtn.isHidden = false
+    }
+}

+ 12 - 9
AIRingtone/Business/TSAIPhotoVC/TSGeneralPicVC/TSGeneralPicVC+Event.swift

@@ -65,7 +65,17 @@ extension TSGeneralPicVC {
     }
 }
 extension TSGeneralPicVC {
+    
+    func updateInfoModel(model:TSActionInfoModel?){
+        if let model = model {
+            infoModel = model
+            self.netWorkImageView.setAsyncImage(urlString: model.response.resultUrl,placeholder:kPlaceholderImage,contentMode: .scaleAspectFill)
+            complete(model)
+        }
+    }
+
     func upDateView(state:TSProgressState,model:TSActionInfoModel?){
+        updateInfoModel(model: model)
         switch state {
             case .failed(let errorStr):
                 showError(text: errorStr)
@@ -132,21 +142,14 @@ extension TSGeneralPicVC {
         saveBtn.isHidden = false
         successTopView.isHidden = false
 
-        imageModel = model
         isClickTheBlankClosePage = true
         
         netWorkImageView.isHidden = false
         regenerateBtn.isHidden = false
-        self.netWorkImageView.setAsyncImage(urlString: model.response.resultUrl,placeholder:kPlaceholderImage,contentMode: .scaleAspectFill){ [weak self] image in
-            guard let self = self else { return }
-        }
-        
+
         kPurchaseToolShared.useOnceForFree(type: vipFreeNumType)
-        
-        if let model = imageModel {
-            complete(model)
-        }
     }
+
 }
 
 

+ 45 - 24
AIRingtone/Business/TSAIPhotoVC/TSGeneralPicVC/TSGeneralPicVC.swift

@@ -7,26 +7,26 @@
 
 class TSGeneralPicVC: TSBottomAlertVC {
     
-    
-
-    var imageModel:TSActionInfoModel?
+    var infoModel:TSActionInfoModel?
     var complete:((TSActionInfoModel)->Void)
     
-    var aiText:String
+    var prompt:String
+    var promptSort:String
     var gennerateType:TSGennerateType
-    init(aiText: String,gennerateType:TSGennerateType,complete:@escaping ((TSActionInfoModel)->Void)) {
-        self.aiText = aiText
+    init(prompt: String,promptSort: String,gennerateType:TSGennerateType,complete:@escaping ((TSActionInfoModel)->Void)) {
+        self.prompt = prompt
+        self.promptSort = promptSort
         self.gennerateType = gennerateType
         self.complete = complete
         super.init()
     }
     
-    lazy var viewModel: TSGenneralPicVM = {
-        let viewModel:TSGenneralPicVM = TSGenneralPicVM()
-        viewModel.aiText = aiText
-        viewModel.gennerateType = gennerateType
-        return viewModel
-    }()
+//    lazy var viewModel: TSGenneralPicVM = {
+//        let viewModel:TSGenneralPicVM = TSGenneralPicVM()
+//        viewModel.aiText = aiText
+//        viewModel.gennerateType = gennerateType
+//        return viewModel
+//    }()
     
     lazy var contactsTool: TSContactsTool = {
         let contactsTool = TSContactsTool(targetVC: self)
@@ -48,7 +48,7 @@ class TSGeneralPicVC: TSBottomAlertVC {
         let generateInView = TSCommonloadingView()
         generateInView.timeLabel.text = "~ 1min"
         //generateInView.cancelBtn.addTarget(self, action: #selector(closePage), for: .touchUpInside)
-        generateInView.backstageBtn.addTarget(self, action: #selector(closePage), for: .touchUpInside)
+        generateInView.backstageBtn.addTarget(self, action: #selector(clickBackstageBtn), for: .touchUpInside)
         return generateInView
     }()
 
@@ -83,19 +83,19 @@ class TSGeneralPicVC: TSBottomAlertVC {
     }
     
     @objc func clickBackstageBtn() {
-        viewModel.cancelAllRequest()
+//        viewModel.cancelAllRequest()
         self.dismiss(animated: true, completion: nil)
     }
     
-    @objc override func closePage() {
-        viewModel.cancelAllRequest()
-        self.dismiss(animated: true, completion: nil)
-    }
+//    @objc override func closePage() {
+//        viewModel.cancelAllRequest()
+//        self.dismiss(animated: true, completion: nil)
+//    }
     
     override func clickAgainBtn() {
         //判断 vip
         if kPurchaseToolShared.kJudgeVipFreeType(vipFreeNumType: vipFreeNumType, vc: self){ return }
-        viewModel.creatImageEmoji(text:aiText)
+        creatImage()
     }
     
     @objc override func clickSubmitBtn(){
@@ -122,11 +122,32 @@ class TSGeneralPicVC: TSBottomAlertVC {
     override func dealThings() {
         //判断 vip
         if kPurchaseToolShared.kJudgeVipFreeType(vipFreeNumType: vipFreeNumType, vc: self){ return }
-        viewModel.creatImageEmoji(text: self.aiText)
-        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)
+//        viewModel.creatImageEmoji(text: self.aiText)
+//        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)
+        creatImage()
+    }
+    
+    func creatImage() {
+        
+        if gennerateType == .poster {
+            let operation:TSGeneratePosterOperation = TSGeneratePosterOperationQueue.shared.creatOperation(uuid: UUID().uuidString) as! TSGeneratePosterOperation
+            operation.$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)
+            operation.creatPoster(oldModel: self.infoModel, prompt: prompt, promptSort: promptSort)
+
+        }else if gennerateType == .photo {
+            let operation:TSGeneratePhotoOperation = TSGeneratePhotoOperationQueue.shared.creatOperation(uuid: UUID().uuidString) as! TSGeneratePhotoOperation
+            operation.$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)
+            operation.creatPhoto(oldModel: self.infoModel, prompt: prompt, promptSort: promptSort)
+        }
     }
 }
 

+ 22 - 5
AIRingtone/Business/TSAIPhotoVC/TSTextGeneralPicVC/TSTextGeneralPicVC.swift

@@ -137,6 +137,10 @@ class TSTextGeneralPicVC: TSBaseVC {
     
     override func dealThings() {
 
+        NotificationCenter.default.addObserver(forName: .kBaseOperationQueueCountChanged, object: nil, queue: nil) { [weak self] notification in
+            guard let self = self else { return }
+            setCreatBtnEnabled()
+        }
     }
     
     func updateVipView() {
@@ -175,16 +179,29 @@ extension TSTextGeneralPicVC {
 }
 extension TSTextGeneralPicVC {
     func generateImage() {
-
-        let gennerateVC = TSGeneralPicVC(aiText: viewModel.prompt, gennerateType: viewModel.gennerateType)
-        {[weak self] model in
+        let gennerateVC = TSGeneralPicVC(prompt: viewModel.prompt, promptSort: viewModel.promptSort, gennerateType: viewModel.gennerateType) { [weak self] model in
             guard let self = self else { return }
             model.request.promptSort = viewModel.promptText
-            viewModel.saveModel(model:model)
-            NotificationCenter.default.post(name: .kReloadUIData, object: nil, userInfo: ["TSGennerateType": viewModel.gennerateType])
+//            viewModel.saveModel(model:model)
+//            NotificationCenter.default.post(name: .kReloadUIData, object: nil, userInfo: ["TSGennerateType": viewModel.gennerateType])
         }
         
         kPresentModalVC(target: self, modelVC: gennerateVC,transitionStyle: .crossDissolve)
     }
+    
+    func setCreatBtnEnabled() {
+        var isAvailability = TSGeneratePosterOperationQueue.shared.isAvailability
+        if vipFreeNumType == .photo{
+            isAvailability = TSGeneratePhotoOperationQueue.shared.isAvailability
+        }
+        
+        if viewModel.isCanGennerate,isAvailability {
+            creatBtnView.setBtnEnabled(isEnabled: true)
+            creatBtnView.loadingAnimation(loading: false)
+        }else{
+            creatBtnView.setBtnEnabled(isEnabled: false)
+            creatBtnView.loadingAnimation(loading: !isAvailability)
+        }
+    }
 
 }

+ 11 - 7
AIRingtone/Business/TSAIPhotoVC/TSTextGeneralPicVC/VM/TSTextGeneralPicVM.swift

@@ -87,12 +87,16 @@ extension TSTextGeneralPicVM {
         }
         return prompt
     }
-
-    func saveModel(model:TSActionInfoModel){
-        if gennerateType == .poster {
-            TSPosterHistory.saveModel(model: model)
-        }else if gennerateType == .photo{
-            TSPhotoHistory.saveModel(model: model)
-        }
+    
+    var promptSort:String{
+        return promptText
     }
+
+//    func saveModel(model:TSActionInfoModel){
+//        if gennerateType == .poster {
+//            TSPosterHistory.saveModel(model: model)
+//        }else if gennerateType == .photo{
+//            TSPhotoHistory.saveModel(model: model)
+//        }
+//    }
 }

+ 5 - 5
AIRingtone/Business/TSAIRintoneVC/TSAIRintoneVC/View/TSAIRintoneHistoryCell.swift

@@ -101,7 +101,7 @@ class TSAIRintoneHistoryCell: SwipeCollectionViewCell  {
     var model:TSActionInfoModel?{
         didSet{
             guard let model = model else { return }
-            cancellable.removeAll()
+//            cancellable.removeAll()
             if let rintoneOperation = TSGenerateRintoneOperationQueue.shared.findOperation(uuid: model.uuid) as? TSGenerateRintoneOperation {
                 DispatchQueue.main.async {
                     rintoneOperation.currentActionInfoModelChanged =
@@ -137,7 +137,7 @@ class TSAIRintoneHistoryCell: SwipeCollectionViewCell  {
             generateView.isHidden = false
             generateView.setProgress(progress: model.percent)
         case .success:
-            cancellable.removeAll()
+//            cancellable.removeAll()
             generateView.isHidden = true
             
             ringView.timeLab.text = Float(model.request.duration).floatToMinuteSecond()
@@ -149,7 +149,7 @@ class TSAIRintoneHistoryCell: SwipeCollectionViewCell  {
             self.changePlayerState(state: TSBusinessAudioPlayer.shared.currentPlayerState)
             
         case .failed:
-            cancellable.removeAll()
+//            cancellable.removeAll()
             generateView.isHidden = false
             generateView.setFail()
         }
@@ -159,7 +159,7 @@ class TSAIRintoneHistoryCell: SwipeCollectionViewCell  {
         didSet{
             guard let ringModel = ringModel else { return }
             
-            cancellable.removeAll()
+//            cancellable.removeAll()
             
             ringView.timeLab.text = Float(ringModel.duration).floatToMinuteSecond()
             ringView.nameLab.text = ringModel.title
@@ -246,7 +246,7 @@ class TSAIRintoneHistoryCell: SwipeCollectionViewCell  {
     
     override func prepareForReuse() {
         super.prepareForReuse()
-        cancellable.removeAll()
+//        cancellable.removeAll()
     }
     func changePlayerState(state:TSBusinessAudioPlayer.PlayerState){
             if playSelf == false {

+ 0 - 1
AIRingtone/Business/TSAIRintoneVC/TSGeneralRintoneVC/TSGeneralRintoneVC.swift

@@ -94,7 +94,6 @@ class TSGeneralRintoneVC: TSBottomAlertVC {
     override func clickAgainBtn() {
         audioPlayer.stop()
         if kPurchaseToolShared.kJudgeVipFreeType(vipFreeNumType: .ringtones, vc: self){ return }//判断 vip
-//        viewModel.creatRintone(text:aiText)
         creatRintone()
     }
     

+ 0 - 5
AIRingtone/Business/TSAIRintoneVC/TSTextGeneralRintoneVC/TSTextGeneralRintoneVC.swift

@@ -106,11 +106,6 @@ class TSTextGeneralRintoneVC: TSBaseVC {
             guard let self = self else { return }
             setCreatBtnEnabled()
         }
-
-        TSGenerateRintoneOperationQueue.shared.rintoneOperationStateChanged = { [weak self] uuid in
-            guard let self = self else { return }
-            refreshGeneratedBlock?(maxCount)
-        }
     }
 
     @objc func clickView() {

+ 4 - 0
AIRingtone/Business/TSAIRintoneVC/TSTextGeneralRintoneVC/VM/TSTextGeneralRintoneVM.swift

@@ -81,4 +81,8 @@ extension TSTextGeneralRintoneVM {
         }
         return prompt
     }
+    
+    var promptSort:String{
+        return promptText
+    }
 }

+ 8 - 1
AIRingtone/Common/Ex/Notification+TSEx.swift

@@ -16,8 +16,15 @@ extension Notification.Name {
     static let kVipFreeNumChanged = Notification.Name("kVipFreeNumChanged")   //Vip免费次数发生变化
     
     static let kGenerateRintoneOperationStart = Notification.Name("kGenerateRintoneOperationStart") //生成铃声任务开发
-    static let kGenerateRintoneOperationChanged = Notification.Name("kGenerateRintoneOperationChanged") //生成铃声任务发生变化
+    
+    
+    
+    
     static let kBaseOperationQueueCountChanged = Notification.Name("kBaseOperationQueueCountChanged") //任务数量放生变化
+    static let kGenerateRintoneOperationChanged = Notification.Name("kGenerateRintoneOperationChanged") //生成铃声任务发生变化
+    static let kGeneratePosterOperationChanged = Notification.Name("kGeneratePosterOperationChanged") //生成海报任务发生变化
+    static let kGeneratePhotoOperationChanged = Notification.Name("kGeneratePhotoOperationChanged") //生成图片任务发生变化
+    
 }
 
 

+ 239 - 0
AIRingtone/Common/Tool/OperationQueue/TSGeneratePhotoOperation/TSGeneratePhotoOperation.swift

@@ -0,0 +1,239 @@
+//
+//  TSGeneratePhotoOperation.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/24.
+//
+import Combine
+import Alamofire
+class TSGeneratePhotoOperationQueue: TSBaseOperationQueue {
+    static let shared:TSGeneratePhotoOperationQueue = TSGeneratePhotoOperationQueue()
+    
+    
+    // 存储每个操作的 AnyCancellable
+    private var stateables: [String: AnyCancellable] = [:]
+    
+    var photoOperationStateChanged:((String)->Void)?
+    
+
+    func creatOperation(uuid: String) -> TSGeneratePhotoOperation {
+        let operation = super.creatOperation(uuid: uuid, type: TSGeneratePhotoOperation.self)
+        if let photoOperation = operation as? TSGeneratePhotoOperation {
+            stateables[uuid] = photoOperation.$stateDatauPblished.sink { [weak self] state in
+                guard let self = self else { return }
+                DispatchQueue.main.async {
+                    self.photoOperationStateChanged?(uuid)
+                    
+                    let uuidData = self.getUUIDData(uuid: uuid)
+                    NotificationCenter.default.post(
+                        name: .kGeneratePhotoOperationChanged,
+                        object: nil,
+                        userInfo: [
+                            "uuid": uuid,
+                            "count":self.queue.maxConcurrentOperationCount,
+                            "state":uuidData.0,
+                            "actionInfo":uuidData.1,
+                        ])
+                }
+            }
+        }
+        return operation as! TSGeneratePhotoOperation
+    }
+    
+    func getUUIDData(uuid:String)->(TSProgressState,TSActionInfoModel?){
+        if let photoOperation = TSGeneratePhotoOperationQueue.shared.findOperation(uuid: uuid) as? TSGeneratePhotoOperation {
+            dePrint("TSBaseOperation stateDatauPblished 发送 = \(photoOperation.stateDatauPblished)")
+            return (photoOperation.stateDatauPblished.0,photoOperation.currentActionInfoModel)
+        }
+        return (.none,TSActionInfoModel())
+    }
+    
+    override func cancelOperations(uuid: String) {
+        super.cancelOperations(uuid: uuid)
+        stateables.removeValue(forKey: uuid)
+    }
+}
+
+class TSGeneratePhotoOperation: TSBaseOperation , @unchecked Sendable{
+    
+    let actionInfoDictPhoto:[String:Any] = [
+        "actionType":"image_create",
+        "comments": "Success",
+        "costTime":7,
+        "createdTimestamp":1742183588,
+        "id":2450,
+        "percent":1,
+        "request":"{\"prompt\": \"Steampunk floating library with brass gears and clockwork mechanisms, leather-bound books flying through amber-lit fog, Victorian-era architecture blended with retro-futurism, 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\", \"width\": 800, \"height\": 800, \"countryCode\": \"US\"}",
+        "response":"{\"resultUrl\": \"https://be-aigc.s3-accelerate.amazonaws.com/8b7fcac9-c691-4c3a-b497-401204fad3e9.png\"}",
+        "status":"success"
+    ]
+    
+    @Published var stateDatauPblished:(TSProgressState,TSActionInfoModel?) = (TSProgressState.none,nil){
+        didSet{
+            dePrint("TSBaseOperation stateDatauPblished didSet = \(stateDatauPblished)")
+            if case .start = stateDatauPblished.0 {
+                start()
+            }else if stateDatauPblished.0.isResult {
+                finished()
+            }
+        }
+    }
+    var gennerateType:TSGennerateType = .photo
+    private var creatRequest:Request?
+    private var queryRequest:Request?
+    private var stopNetwork = false
+    private var generatingProgress = 0
+    private var aiText:String = ""
+    private var action_id:Int = 0
+   
+    var currentActionInfoModelChanged:((TSActionInfoModel)->Void)?
+    @Published  var currentActionInfoModel: TSActionInfoModel = TSActionInfoModel()
+
+    func replaceSaveInfoModel(model:TSActionInfoModel){
+        model.uuid = uuid
+        TSPhotoHistory.replaceModel(oldID: currentActionInfoModel.id, newModel: model)
+        currentActionInfoModel = model
+        dePrint("TSPhotoHistory.listModelArray.count=\(TSPhotoHistory.listModelArray.count)")
+        dePrint("model actionStatus 发出=\(model.actionStatus)")
+        currentActionInfoModelChanged?(currentActionInfoModel)
+    }
+    
+    //模拟数据
+    func creatPhoto(oldModel:TSActionInfoModel? = nil,prompt:String,promptSort:String) {
+        stateDatauPblished = (.start,nil)
+        if let model = oldModel {
+            currentActionInfoModel = model
+        }else {
+            currentActionInfoModel.id = Int.timestampInt()
+            currentActionInfoModel.request.prompt = prompt
+            currentActionInfoModel.request.promptSort = promptSort
+            currentActionInfoModel.actionStatus = .pending
+            currentActionInfoModel.status = "pending"
+        }
+        
+        replaceSaveInfoModel(model: currentActionInfoModel)
+        stateDatauPblished = (.start,currentActionInfoModel)
+        let time = 5.0
+        
+        for i in 0..<Int(time){
+            kDelayOnMainThread(Double(i)) {
+                let progress = Float(i)/100.0
+                self.currentActionInfoModel.percent = progress
+                self.currentActionInfoModel.actionStatus = .running
+                self.currentActionInfoModel.status = "running"
+                self.replaceSaveInfoModel(model: self.currentActionInfoModel)
+                self.stateDatauPblished = (.progressString(self.generating(progress: progress)),nil)
+            }
+        }
+
+        kDelayOnMainThread(time+1.0) {
+            if Bool.random(), let infoModel = TSActionInfoModel(JSON: self.actionInfoDictPhoto){
+                infoModel.id = Int.uuid
+                self.replaceSaveInfoModel(model: infoModel)
+                self.stateDatauPblished = (.success(nil),self.currentActionInfoModel)
+            }else{
+                self.currentActionInfoModel.actionStatus = .failed
+                self.currentActionInfoModel.status = "failed"
+                self.replaceSaveInfoModel(model: self.currentActionInfoModel)
+                self.stateDatauPblished = (.failed("error?.localizedDescription"),nil)
+            }
+//            TSPhotoHistory.dePrintAllModel()
+        }
+    }
+    
+
+    
+//    //width 和 height 必须是 32 的倍数
+//    func creatImageEmoji(text:String) {
+//        generatingProgress = 0
+//        aiText = text
+//        let postDict:[String : Any] = [
+//            "prompt":text,
+//            "width":textPicW,
+//            "height":textPicH
+//        ]
+//        stateDatauPblished = (.start,nil)
+//        stateDatauPblished = (.progressString(generating(progress: 0.0)),nil)
+//        creatRequest = TSNetworkShared.post(urlType: .textPicCreate,parameters: postDict) { [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 = TSActionInfoModel(JSON: result) {
+                    switch genmojiModel.actionStatus {
+                    case .success:
+                        TSToastShared.hideLoading()
+                        self.stateDatauPblished = (.success(nil),genmojiModel)
+                        generatingProgress = 0
+                    case .failed:
+                        self.stateDatauPblished = (.failed(kNetWorkMessage(data: data) ?? ""),nil)
+                        generatingProgress = 0
+                    default:
+                        stateDatauPblished = (.progressString(generating(progress: genmojiModel.percent)),nil)
+                        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 generating(progress:Float) -> String {
+
+        //Generating 0%-100%
+        var progressInt = Int(progress*100)
+
+        if generatingProgress >= progressInt{
+            return generatingString
+        }
+
+        if progressInt > 99 {
+            progressInt = 99
+        }
+        
+        generatingProgress = progressInt
+        return generatingString
+    }
+     
+    var generatingString:String {
+        if gennerateType == .photo {
+            return "Working on your contact photo \(generatingProgress)% ..."
+        }
+        return "Working on your contact Photo \(generatingProgress)% ..."
+    }
+    
+    var textPicW:Int{
+        return kTextPicW
+    }
+    
+    var textPicH:Int{
+        if gennerateType == .photo {
+            return kTextPicW
+        }
+        return kTextPicH
+    }
+}

+ 239 - 0
AIRingtone/Common/Tool/OperationQueue/TSGeneratePosterOperation/TSGeneratePosterOperation.swift

@@ -0,0 +1,239 @@
+//
+//  TSGeneratePosterOperation.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/24.
+//
+import Combine
+import Alamofire
+class TSGeneratePosterOperationQueue: TSBaseOperationQueue {
+    static let shared:TSGeneratePosterOperationQueue = TSGeneratePosterOperationQueue()
+    
+    
+    // 存储每个操作的 AnyCancellable
+    private var stateables: [String: AnyCancellable] = [:]
+    
+    var PosterOperationStateChanged:((String)->Void)?
+    
+
+    func creatOperation(uuid: String) -> TSGeneratePosterOperation {
+        let operation = super.creatOperation(uuid: uuid, type: TSGeneratePosterOperation.self)
+        if let PosterOperation = operation as? TSGeneratePosterOperation {
+            stateables[uuid] = PosterOperation.$stateDatauPblished.sink { [weak self] state in
+                guard let self = self else { return }
+                DispatchQueue.main.async {
+                    self.PosterOperationStateChanged?(uuid)
+                    
+                    let uuidData = self.getUUIDData(uuid: uuid)
+                    NotificationCenter.default.post(
+                        name: .kGeneratePosterOperationChanged,
+                        object: nil,
+                        userInfo: [
+                            "uuid": uuid,
+                            "count":self.queue.maxConcurrentOperationCount,
+                            "state":uuidData.0,
+                            "actionInfo":uuidData.1,
+                        ])
+                }
+            }
+        }
+        return operation as! TSGeneratePosterOperation
+    }
+    
+    func getUUIDData(uuid:String)->(TSProgressState,TSActionInfoModel?){
+        if let PosterOperation = TSGeneratePosterOperationQueue.shared.findOperation(uuid: uuid) as? TSGeneratePosterOperation {
+            dePrint("TSBaseOperation stateDatauPblished 发送 = \(PosterOperation.stateDatauPblished)")
+            return (PosterOperation.stateDatauPblished.0,PosterOperation.currentActionInfoModel)
+        }
+        return (.none,TSActionInfoModel())
+    }
+    
+    override func cancelOperations(uuid: String) {
+        super.cancelOperations(uuid: uuid)
+        stateables.removeValue(forKey: uuid)
+    }
+}
+
+class TSGeneratePosterOperation: TSBaseOperation , @unchecked Sendable{
+    
+    let actionInfoDictPoster:[String:Any] = [
+        "actionType":"image_create",
+        "comments": "Success",
+        "costTime":9,
+        "createdTimestamp":1742183242,
+        "id":2449,
+        "percent":1,
+        "request":"{\"prompt\": \"Traditional Chinese ink painting style phoenix soaring through misty mountain peaks, dynamic black brushstrokes with crimson accents on rice paper texture, 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\", \"width\": 800, \"height\": 1440, \"countryCode\": \"FR\"}",
+        "response": "{\"resultUrl\": \"https://be-aigc.s3-accelerate.amazonaws.com/4c946f78-b1e7-4ffe-ba18-fff26b10178c.png\"}",
+        "status":"success"
+    ]
+    
+    @Published var stateDatauPblished:(TSProgressState,TSActionInfoModel?) = (TSProgressState.none,nil){
+        didSet{
+            dePrint("TSBaseOperation stateDatauPblished didSet = \(stateDatauPblished)")
+            if case .start = stateDatauPblished.0 {
+                start()
+            }else if stateDatauPblished.0.isResult {
+                finished()
+            }
+        }
+    }
+    var gennerateType:TSGennerateType = .poster
+    private var creatRequest:Request?
+    private var queryRequest:Request?
+    private var stopNetwork = false
+    private var generatingProgress = 0
+    private var aiText:String = ""
+    private var action_id:Int = 0
+   
+    var currentActionInfoModelChanged:((TSActionInfoModel)->Void)?
+    @Published  var currentActionInfoModel: TSActionInfoModel = TSActionInfoModel()
+
+    func replaceSaveInfoModel(model:TSActionInfoModel){
+        model.uuid = uuid
+        TSPosterHistory.replaceModel(oldID: currentActionInfoModel.id, newModel: model)
+        currentActionInfoModel = model
+        dePrint("TSPosterHistory.listModelArray.count=\(TSPosterHistory.listModelArray.count)")
+        dePrint("model actionStatus 发出=\(model.actionStatus)")
+        currentActionInfoModelChanged?(currentActionInfoModel)
+    }
+
+    //模拟数据
+    func creatPoster(oldModel:TSActionInfoModel? = nil,prompt:String,promptSort:String) {
+        stateDatauPblished = (.start,nil)
+        if let model = oldModel {
+            currentActionInfoModel = model
+        }else {
+            currentActionInfoModel.id = Int.timestampInt()
+            currentActionInfoModel.request.prompt = prompt
+            currentActionInfoModel.request.promptSort = promptSort
+            currentActionInfoModel.actionStatus = .pending
+            currentActionInfoModel.status = "pending"
+        }
+        
+        replaceSaveInfoModel(model: currentActionInfoModel)
+        stateDatauPblished = (.start,currentActionInfoModel)
+        let time = 5.0
+        
+        for i in 0..<Int(time){
+            kDelayOnMainThread(Double(i)) {
+                let progress = Float(i)/100.0
+                self.currentActionInfoModel.percent = progress
+                self.currentActionInfoModel.actionStatus = .running
+                self.currentActionInfoModel.status = "running"
+                self.replaceSaveInfoModel(model: self.currentActionInfoModel)
+                self.stateDatauPblished = (.progressString(self.generating(progress: progress)),nil)
+            }
+        }
+
+        kDelayOnMainThread(time+1.0) {
+            if Bool.random(), let infoModel = TSActionInfoModel(JSON: self.actionInfoDictPoster){
+                infoModel.id = Int.uuid
+                self.replaceSaveInfoModel(model: infoModel)
+                self.stateDatauPblished = (.success(nil),self.currentActionInfoModel)
+            }else{
+                self.currentActionInfoModel.actionStatus = .failed
+                self.currentActionInfoModel.status = "failed"
+                self.replaceSaveInfoModel(model: self.currentActionInfoModel)
+                self.stateDatauPblished = (.failed("error?.localizedDescription"),nil)
+            }
+//            TSPosterHistory.dePrintAllModel()
+        }
+    }
+    
+
+    
+//    //width 和 height 必须是 32 的倍数
+//    func creatImageEmoji(text:String) {
+//        generatingProgress = 0
+//        aiText = text
+//        let postDict:[String : Any] = [
+//            "prompt":text,
+//            "width":textPicW,
+//            "height":textPicH
+//        ]
+//        stateDatauPblished = (.start,nil)
+//        stateDatauPblished = (.progressString(generating(progress: 0.0)),nil)
+//        creatRequest = TSNetworkShared.post(urlType: .textPicCreate,parameters: postDict) { [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 = TSActionInfoModel(JSON: result) {
+                    switch genmojiModel.actionStatus {
+                    case .success:
+                        TSToastShared.hideLoading()
+                        self.stateDatauPblished = (.success(nil),genmojiModel)
+                        generatingProgress = 0
+                    case .failed:
+                        self.stateDatauPblished = (.failed(kNetWorkMessage(data: data) ?? ""),nil)
+                        generatingProgress = 0
+                    default:
+                        stateDatauPblished = (.progressString(generating(progress: genmojiModel.percent)),nil)
+                        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 generating(progress:Float) -> String {
+
+        //Generating 0%-100%
+        var progressInt = Int(progress*100)
+
+        if generatingProgress >= progressInt{
+            return generatingString
+        }
+
+        if progressInt > 99 {
+            progressInt = 99
+        }
+        
+        generatingProgress = progressInt
+        return generatingString
+    }
+     
+    var generatingString:String {
+        if gennerateType == .poster {
+            return "Working on your contact Poster \(generatingProgress)% ..."
+        }
+        return "Working on your contact poster \(generatingProgress)% ..."
+    }
+    
+    var textPicW:Int{
+        return kTextPicW
+    }
+    
+    var textPicH:Int{
+        if gennerateType == .poster {
+            return kTextPicW
+        }
+        return kTextPicH
+    }
+}