100Years 1 month ago
parent
commit
796494fdd0

+ 14 - 2
AIEmoji.xcodeproj/project.pbxproj

@@ -71,6 +71,7 @@
 		A80EDD642D6C3F82003CD332 /* MarkdownStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDD322D6C3F82003CD332 /* MarkdownStyle.swift */; };
 		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 */; };
 		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 */; };
@@ -239,6 +240,7 @@
 		A80EDD402D6C3F82003CD332 /* MarkdownParser+UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MarkdownParser+UIKit.swift"; sourceTree = "<group>"; };
 		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>"; };
 		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>"; };
@@ -796,6 +798,14 @@
 			path = MarkdownKit;
 			sourceTree = "<group>";
 		};
+		A80EDDD72D6D9705003CD332 /* UIStackView */ = {
+			isa = PBXGroup;
+			children = (
+				A80EDDD82D6D9713003CD332 /* TSCustomStackView.swift */,
+			);
+			path = UIStackView;
+			sourceTree = "<group>";
+		};
 		A85E478D2D670DF10018D62D /* TSTextGeneralPictureVC */ = {
 			isa = PBXGroup;
 			children = (
@@ -1070,6 +1080,7 @@
 		A8F774D72D38EA8C00AA6E93 /* View */ = {
 			isa = PBXGroup;
 			children = (
+				A80EDDD72D6D9705003CD332 /* UIStackView */,
 				A8F776292D3A70AA00AA6E93 /* UILabel */,
 			);
 			path = View;
@@ -1628,6 +1639,7 @@
 				A8F7753F2D39340E00AA6E93 /* TSSetingVC.swift in Sources */,
 				A8F7762B2D3A70B200AA6E93 /* PaddedLabel.swift in Sources */,
 				A80E73E62D5348D000C64288 /* SettingPurchaseTopView.swift in Sources */,
+				A80EDDD92D6D9713003CD332 /* TSCustomStackView.swift in Sources */,
 				A80E72382D3F473B00C64288 /* DiyPaperProtocol.swift in Sources */,
 				A8F775382D390C3C00AA6E93 /* TSNetworkManager.swift in Sources */,
 				A85E47982D672AE70018D62D /* TSTextPicGennerateVM.swift in Sources */,
@@ -1664,7 +1676,7 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 1;
+				CURRENT_PROJECT_VERSION = 2;
 				DEVELOPMENT_TEAM = 65UD255J84;
 				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -1703,7 +1715,7 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 1;
+				CURRENT_PROJECT_VERSION = 2;
 				DEVELOPMENT_TEAM = 65UD255J84;
 				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 				GENERATE_INFOPLIST_FILE = YES;

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

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

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


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


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

@@ -16,8 +16,8 @@
                         <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_text" translatesAutoresizingMaskIntoConstraints="NO" id="FfL-7D-71P">
-                                <rect key="frame" x="65" y="325" width="263.33333333333331" height="42"/>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch_image" translatesAutoresizingMaskIntoConstraints="NO" id="FfL-7D-71P">
+                                <rect key="frame" x="65" y="325" width="263.33333333333331" height="62"/>
                             </imageView>
                         </subviews>
                         <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
@@ -34,6 +34,6 @@
         </scene>
     </scenes>
     <resources>
-        <image name="launch_text" width="263.33334350585938" height="42"/>
+        <image name="launch_image" width="263.33334350585938" height="62"/>
     </resources>
 </document>

+ 3 - 3
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+ChatDelegate.swift

@@ -117,9 +117,9 @@ extension TSChatViewController: MessagesDisplayDelegate {
     }
     
     // MARK: - All Messages
-    
+     
     func backgroundColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
-        isFromCurrentSender(message: message) ? .themeColor : "#333333".uiColor
+        isFromCurrentSender(message: message) ? .themeColor : UIColor.white.withAlphaComponent(0.05)//"#222222".uiColor
     }
     
     func messageStyle(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> MessageStyle {
@@ -222,7 +222,7 @@ extension TSChatViewController: MessageCellDelegate {
             if case let .attributedText(text) = model.kind{
                 //拷贝文字到截切板
                 UIPasteboard.general.string = text.string
-                kSavePhotoSuccesswShared.show(atView: self.view,text: "Copy Successfully".localized,showViewBtn:false)
+                kSavePhotoSuccesswShared.show(atView: self.view,text: "Copyed Successfully".localized,showViewBtn:false)
             }
             
         case .refreshMsg:

+ 6 - 2
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+Keyboard.swift

@@ -43,12 +43,16 @@ extension TSChatViewController{
         let offsetY = scrollView.contentOffset.y
         let contentHeight = scrollView.contentSize.height
         let frameHeight = scrollView.frame.size.height
-        
         // 判断是否需要显示滚动到底部的按钮
-        if offsetY > contentHeight - frameHeight + inputContainerView.frame.size.height - 60 {//40
+        let h = contentHeight - frameHeight + inputContainerView.frame.size.height - 60
+        debugPrint("scrollViewDidScroll offsetY=\(offsetY),contentHeight=\(contentHeight),frameHeight=\(frameHeight),h=\(h)")
+        
+        if offsetY > h {
             scrollToBottomButton.isHidden = true
+            debugPrint("scrollViewDidScroll true")
         } else {
             scrollToBottomButton.isHidden = false
+            debugPrint("scrollViewDidScroll false")
         }
         
       

+ 10 - 1
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+SendMsg.swift

@@ -63,7 +63,12 @@ extension TSChatViewController {
         message.sendState = .start
         message.messageType = .aiRobotResponse
         insertMessage(message,indexPath: indexPath)
-        scrollToBottom()
+        
+        kDelayMainShort {
+            self.scrollToBottom()
+        }
+        
+        
         NotificationCenter.default.post(name: .kAIAnsweringNotification, object: nil, userInfo: [kIsAIAnswering: true])
 
         //每次全部输出
@@ -97,6 +102,10 @@ extension TSChatViewController {
 //                kPurchaseDefault.useOnceForFree(type: .aichat)//消耗一次 AI 次数
             }else {
                 message.sendState = .failed(kAIErrorString)
+                if viewModel.AiMDString.count == 0 {
+                    viewModel.AiMDString = kAIErrorString
+                    message.kind = .attributedText(kMDAttributedString(text: viewModel.AiMDString))
+                }
             }
             message.markDownText = viewModel.AiMDString
             updataAIChatCellUI()

+ 2 - 1
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController.swift

@@ -163,12 +163,13 @@ class TSChatViewController: MessagesViewController, MessagesDataSource {
             if isReplace == false {
                 messagesCollectionView.insertItems(at: [cellIndexPaht])
             }
-        
+            
             if messageList.count >= 2 {
                 messagesCollectionView.reloadItems(at: [cellIndexPaht])
             }else{
                 messagesCollectionView.reloadData()
             }
+            
         }, completion: { [weak self] _ in
             if self?.isLastSectionVisible() == true {
                 self?.messagesCollectionView.scrollToLastItem(animated: true)

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

@@ -83,7 +83,8 @@ extension TSAIChatVM {
         if uiStyle == .history {
             return self.dbAIChatList.getMessageList()
         }else {
-            let aiString = "I can tackle your questions, my skillset includes, but is not limited to:\n📧 Composing high-quality emails\n🇺🇸 Facilitating language learning\n📑 Assisting in your studies\n💡Brainstorming ideas\nand much more!"
+//            let aiString = "I can tackle your questions, my skillset includes, but is not limited to:\n**📧 Composing high-quality emails**\n**🇺🇸 Facilitating language learning**\n**📑 Assisting in your studies**\n**💡Brainstorming ideas**\n**and much more!**"
+            let aiString = "I can tackle your questions, my skillset includes, but is not limited to:\n`📧 Composing high-quality emails`\n`🇺🇸 Facilitating language learning`\n`📑 Assisting in your studies`\n`💡Brainstorming ideas`\n`and much more!`"
             let msg = TSChatMessage(kind: .attributedText(kMDAttributedString(text: aiString)), user: kAIUser, messageId: "", date: Date())
 
             let model = TSChatMessageUIBaseModel()

+ 80 - 8
AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSMarkDownTool.swift

@@ -5,14 +5,15 @@
 //  Created by 100Years on 2025/2/23.
 //
 
-let kLineSpacing = 8.0
-let KFont = UIFont.font(size: 16)
-let kSendColor = "#111111".uiColor
+private let kLineSpacing = 6.0
+private let KFont = UIFont.font(size: 16)
+private let kSendColor = "#111111".uiColor
 
 
-let paragraphStyle:NSMutableParagraphStyle = {
+private let paragraphStyle:NSMutableParagraphStyle = {
     let paragraphStyle = NSMutableParagraphStyle()
     paragraphStyle.lineSpacing = kLineSpacing
+    paragraphStyle.paragraphSpacing = 1
     return paragraphStyle
 }()
 
@@ -29,15 +30,31 @@ func kMDSendAttributedString(text:String) -> NSAttributedString{
 }
 
 
-var md: MarkdownParser = {
-    let parser = MarkdownParser(font: KFont,color: .white)
+private var md: MarkdownParser = {
+    let parser = MarkdownParser(font: KFont,color: .white.withAlphaComponent(0.9))//普通文本颜色和字体大小
     parser.enabledElements = .disabledAutomaticLink
+    
+
+    //代码
     let code = TSCustomMarkdownCode()
     code.textBackgroundColor = .clear
-    
-    let codeColor = UIColor.white.withAlphaComponent(0.8)
+    let codeColor = UIColor.white
     code.color = codeColor
     code.textHighlightColor = codeColor
+    code.font = .font(size: 16,weight: UIFont.Weight.medium)
+    
+    //粗体
+    parser.bold.font = .font(size: 18)
+    parser.bold.color = UIColor.white
+    
+    //标题
+    parser.header.maxLevel = 3
+    parser.header.font = .font(size: 22)
+    parser.header.fontIncrease = 2
+    parser.header.color = UIColor.white
+    
+
+
     
     parser.replaceDefaultElement(parser.code, with: code)
     return parser
@@ -50,6 +67,8 @@ func kMDAttributedString(text:String) -> NSAttributedString{
     return attributedString
 }
 
+
+
 open class TSCustomMarkdownCode: MarkdownCode {
   fileprivate static let regex = "(^|\\s?)(\\`{1,3})(.+?)(\\2)"
   open override var regex: String {
@@ -59,6 +78,59 @@ open class TSCustomMarkdownCode: MarkdownCode {
 
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 //import SwiftyMarkdown
 //private var md: SwiftyMarkdown = {
 //

+ 168 - 71
AIEmoji/Business/AIChat/TSChatViewController/Views/TSCellView/TSChatMsgToolView.swift

@@ -9,19 +9,19 @@
 //30
 class TSChatMsgToolView: TSChatMsgBaseView {
     var viewHeight = TSLayoutSizeCalculator.cellMessageToolViewH
-
-    var isSuccess:Bool = true {
+    //是否显示失败
+    var isShowRefresh:Bool = false {
         didSet{
-            if isSuccess {
-                successView.isHidden = false
-                failView.isHidden = true
-            }else {
-                successView.isHidden = true
-                failView.isHidden = false
-            }
+            updateRefreshBtn()
         }
     }
-    
+    //是否只显示失败
+    var isOnlyShowRefresh:Bool = false {
+        didSet{
+            updateNotRefreshBtn()
+        }
+    }
+
     lazy var copyBtn: UIButton = {
         let copyBtn = UIButton.createButton(image: UIImage(named: "aichat_copy")) { [weak self]  in
             guard let self = self else { return }
@@ -29,85 +29,182 @@ class TSChatMsgToolView: TSChatMsgBaseView {
         }
         return copyBtn
     }()
-    lazy var successView: UIView = {
-        let successView = UIView()
-        
-        let copyBtn = UIButton.createButton(image: UIImage(named: "aichat_copy")) { [weak self]  in
-            guard let self = self else { return }
-            didTapCustomViewBlock?(.copyMsg)
-        }
     
-        successView.addSubview(copyBtn)
-        copyBtn.snp.makeConstraints { make in
-            make.leading.equalTo(12)
-            make.top.equalToSuperview()
-            make.width.height.equalTo(20)
-        }
-        
-        return successView
-    }()
     
-    lazy var failView: UIView = {
-        let failView = UIView()
-        
-        let refreshBtn = UIButton.createButton(image: UIImage(named: "refresh")) { [weak self]  in
+    
+    lazy var refreshBtn: UIButton = {
+        let refreshBtn = UIButton.createButton(image: UIImage(named: "refresh_gary")) { [weak self]  in
             guard let self = self else { return }
             didTapCustomViewBlock?(.refreshMsg)
         }
-    
-        failView.addSubview(refreshBtn)
-        refreshBtn.snp.makeConstraints { make in
-            make.leading.equalTo(12)
-            make.top.equalToSuperview()
-            make.width.height.equalTo(20)
-        }
-        
-        return failView
+        return refreshBtn
     }()
     
-    
+
+    lazy var stackView: TSCustomStackView = {
+        let stackView = TSCustomStackView(axis: .horizontal,spacing: 10)
+        return stackView
+    }()
+
     override func creatUI() {
-        
+
         self.clipsToBounds = true
-//        contentView.backgroundColor = .green
-        contentView.addSubview(successView)
-        successView.snp.makeConstraints { make in
-            make.leading.equalTo(0)
-            make.trailing.equalTo(0)
-            make.top.bottom.equalToSuperview()
-            make.height.equalTo(viewHeight)
-        }
         
-        contentView.addSubview(failView)
-        failView.snp.makeConstraints { make in
-            make.leading.equalTo(0)
-            make.trailing.equalTo(0)
-            make.top.bottom.equalToSuperview()
-            make.height.equalTo(viewHeight)
+        contentView.addSubview(stackView)
+        stackView.snp.makeConstraints { make in
+            make.leading.equalTo(12)
+            make.top.equalTo(0)
+            make.height.equalTo(20)
         }
         
-//        setUpSuccess()
+//        stackView.addSubviewToStack(copyBtn)
+//        copyBtn.snp.makeConstraints { make in
+//            make.width.equalTo(20)
+//        }
+        
     }
 
-//    func setUpSuccess(){
+    func updateRefreshBtn() {
+        if isShowRefresh {
+            stackView.insertViewToStack(refreshBtn, at: 0)
+            refreshBtn.snp.makeConstraints { make in
+                make.width.equalTo(20)
+            }
+        }else{
+            stackView.removeViewToStack(refreshBtn)
+        }
+    }
+    
+    
+    func updateNotRefreshBtn(){
+        if isOnlyShowRefresh {
+            stackView.removeViewToStack(copyBtn)
+        }else{
+            if copyBtn.superview == nil {
+                stackView.addSubviewToStack(copyBtn)
+                copyBtn.snp.makeConstraints { make in
+                    make.width.equalTo(20)
+                }
+            }
+        }
+    }
+
+//    func setHidden(isHidden:Bool) {
+//        self.isHidden = isHidden
+//        let h = isHidden ? 0 : viewHeight
+//
+//        successView.snp.updateConstraints { make in
+//            make.height.equalTo(h)
+//        }
+//
+//        failView.snp.updateConstraints { make in
+//            make.height.equalTo(h)
+//        }
+//    }
+}
+
+
+//class TSChatMsgToolView: TSChatMsgBaseView {
+//    var viewHeight = TSLayoutSizeCalculator.cellMessageToolViewH
+//
+//    var isSuccess:Bool = true {
+//        didSet{
+//            if isSuccess {
+//                successView.isHidden = false
+//                failView.isHidden = true
+//            }else {
+//                successView.isHidden = true
+//                failView.isHidden = false
+//            }
+//        }
+//    }
+//    
+//    lazy var copyBtn: UIButton = {
+//        let copyBtn = UIButton.createButton(image: UIImage(named: "aichat_copy")) { [weak self]  in
+//            guard let self = self else { return }
+//            didTapCustomViewBlock?(.copyMsg)
+//        }
+//        return copyBtn
+//    }()
+//    lazy var successView: UIView = {
+//        let successView = UIView()
+//        
+//        let copyBtn = UIButton.createButton(image: UIImage(named: "aichat_copy")) { [weak self]  in
+//            guard let self = self else { return }
+//            didTapCustomViewBlock?(.copyMsg)
+//        }
+//    
 //        successView.addSubview(copyBtn)
 //        copyBtn.snp.makeConstraints { make in
 //            make.leading.equalTo(12)
 //            make.top.equalToSuperview()
 //            make.width.height.equalTo(20)
 //        }
+//        
+//        return successView
+//    }()
+//    
+//    lazy var failView: UIView = {
+//        let failView = UIView()
+//        
+//        let refreshBtn = UIButton.createButton(image: UIImage(named: "refresh")) { [weak self]  in
+//            guard let self = self else { return }
+//            didTapCustomViewBlock?(.refreshMsg)
+//        }
+//    
+//        failView.addSubview(refreshBtn)
+//        refreshBtn.snp.makeConstraints { make in
+//            make.leading.equalTo(12)
+//            make.top.equalToSuperview()
+//            make.width.height.equalTo(20)
+//        }
+//        
+//        return failView
+//    }()
+//    
+//    
+//    override func creatUI() {
+//        
+//        self.clipsToBounds = true
+////        contentView.backgroundColor = .green
+//        contentView.addSubview(successView)
+//        successView.snp.makeConstraints { make in
+//            make.leading.equalTo(0)
+//            make.trailing.equalTo(0)
+//            make.top.bottom.equalToSuperview()
+//            make.height.equalTo(viewHeight)
+//        }
+//        
+//        contentView.addSubview(failView)
+//        failView.snp.makeConstraints { make in
+//            make.leading.equalTo(0)
+//            make.trailing.equalTo(0)
+//            make.top.bottom.equalToSuperview()
+//            make.height.equalTo(viewHeight)
+//        }
+//        
+////        setUpSuccess()
 //    }
-    
-    func setHidden(isHidden:Bool) {
-        self.isHidden = isHidden
-        let h = isHidden ? 0 : viewHeight
-    
-        successView.snp.updateConstraints { make in
-            make.height.equalTo(h)
-        }
-        
-        failView.snp.updateConstraints { make in
-            make.height.equalTo(h)
-        }
-    }
-}
+//
+////    func setUpSuccess(){
+////        successView.addSubview(copyBtn)
+////        copyBtn.snp.makeConstraints { make in
+////            make.leading.equalTo(12)
+////            make.top.equalToSuperview()
+////            make.width.height.equalTo(20)
+////        }
+////    }
+//    
+//    func setHidden(isHidden:Bool) {
+//        self.isHidden = isHidden
+//        let h = isHidden ? 0 : viewHeight
+//    
+//        successView.snp.updateConstraints { make in
+//            make.height.equalTo(h)
+//        }
+//        
+//        failView.snp.updateConstraints { make in
+//            make.height.equalTo(h)
+//        }
+//    }
+//}

+ 54 - 29
AIEmoji/Business/AIChat/TSChatViewController/Views/TSMessageContentCell.swift

@@ -188,6 +188,7 @@ class TSMessageContentCell: MessageCollectionViewCell {
         messageBottomContainerView.addSubview(msgToolView)
         msgToolView.snp.makeConstraints { make in
             make.leading.trailing.top.bottom.equalTo(0)
+            make.height.equalTo(msgToolView.viewHeight)
         }
         
     }
@@ -290,25 +291,34 @@ class TSMessageContentCell: MessageCollectionViewCell {
     func handelMsgToolView(message: MessageType,dataSource: MessagesDataSource){
         if let msgModel = message as? TSChatMessage,let chatVC = dataSource as? TSChatViewController{
     
-            var isSuccess = msgModel.sendState.reslutSuccess
-            let isLast = isLast(message: message, dataSource: dataSource)
-            
-            //重试只在显示在最后一个 cell,所以cell 是失败了,只要不是最后一个 cell,都要显示成功
-            //历史记录中,不能显示重试
-            if isLast == false || chatVC.viewModel.uiStyle == .history {
-                isSuccess = true
-            }
-            //成功显示复制,失败显示重试
-            msgToolView.isSuccess = isSuccess
-            
-            var isHidden = true
-            if msgModel.messageType == TSChatMessageMsgType.aiRobotResponse,
-               msgModel.sendState.isResult{
-                //是机器人的回答,且回答已经结束,就可以显示工具栏了
-                isHidden = false
-            }
+            if msgModel.sendState.isResult {
+                var isHiddenReload = true
 
-            self.msgToolView.setHidden(isHidden: isHidden)
+                let isLast = kIsChatLast(message: message, dataSource: dataSource)
+                
+                //重试只在显示在最后一个 cell,历史记录中,不能显示重试
+                if isLast == true , chatVC.viewModel.uiStyle != .history {
+                    isHiddenReload = false
+                }
+                
+                //成功显示复制,失败显示重试
+                msgToolView.isShowRefresh = !isHiddenReload
+                msgToolView.isOnlyShowRefresh = (msgModel.sendState.reslutSuccess == false)
+                
+                var isHidden = true
+                if msgModel.messageType == TSChatMessageMsgType.aiRobotResponse,
+                   msgModel.sendState.isResult{
+                    //是机器人的回答,且回答已经结束,就可以显示工具栏了
+                    isHidden = false
+                }
+                msgToolView.snp.updateConstraints { make in
+                    make.height.equalTo(isHidden ? 0 : msgToolView.viewHeight)
+                }
+            }else{
+                msgToolView.snp.updateConstraints { make in
+                    make.height.equalTo(0)
+                }
+            }
         }
     }
     
@@ -337,17 +347,32 @@ extension TSMessageContentCell {
     }
     
     
-    func isLast(message: MessageType,dataSource: MessagesDataSource)->Bool{
-        if let chatVC = dataSource as? TSChatViewController,
-           let msg = message as? TSChatMessage
-        {
-            let array = chatVC.messageList
-            // 判断对象是否在数组中是最后一个元素
-            if let index = array.firstIndex(where: { $0 === msg }), index == array.count - 1 {
-                return true
-            }
+//    func isLast(message: MessageType,dataSource: MessagesDataSource)->Bool{
+//        if let chatVC = dataSource as? TSChatViewController,
+//           let msg = message as? TSChatMessage
+//        {
+//            let array = chatVC.messageList
+//            // 判断对象是否在数组中是最后一个元素
+//            if let index = array.firstIndex(where: { $0 === msg }), index == array.count - 1 {
+//                return true
+//            }
+//        }
+//        
+//        return false
+//    }
+}
+
+
+func kIsChatLast(message: MessageType,dataSource: MessagesDataSource)->Bool{
+    if let chatVC = dataSource as? TSChatViewController,
+       let msg = message as? TSChatMessage
+    {
+        let array = chatVC.messageList
+        // 判断对象是否在数组中是最后一个元素
+        if let index = array.firstIndex(where: { $0 === msg }), index == array.count - 1 {
+            return true
         }
-        
-        return false
     }
+    
+    return false
 }

+ 4 - 3
AIEmoji/Business/VIewTool/TSViewTool.swift

@@ -51,7 +51,7 @@ class TSSavePhotoSuccessTool {
     
     private lazy var textLabel:UILabel = {
         let textLabel = UILabel()
-        textLabel.textColor = "#4A5178".uiColor
+        textLabel.textColor = UIColor.white
         textLabel.text = "Save Successfully".localized
         textLabel.font = UIFont.font(size: 14)
         return textLabel
@@ -63,7 +63,8 @@ class TSSavePhotoSuccessTool {
     
     
     private lazy var viewButton:UIView = {
-        let viewButton = UIButton.createButton(title: "View".localized ,backgroundColor: "4FEA9D".toColor()?.withAlphaComponent(0.2),font: UIFont.font(size: 14),titleColor: "4FEA9D".toColor(),corner: 14) {
+        let color = "4FEA9D".uiColor
+        let viewButton = UIButton.createButton(title: "View".localized ,backgroundColor: color.withAlphaComponent(0.1),font: UIFont.font(size: 14),titleColor: color,corner: 14) {
             if let url = URL(string: "photos-redirect://") {
                 if UIApplication.shared.canOpenURL(url) {
                     UIApplication.shared.open(url, options: [:], completionHandler: nil)
@@ -85,7 +86,7 @@ class TSSavePhotoSuccessTool {
         
         // 圆角
         let colorBg = UIView()
-        colorBg.backgroundColor = .white
+        colorBg.backgroundColor = "#333333".uiColor
         colorBg.layer.cornerRadius = 8
         colorBg.layer.masksToBounds = true
         colorBg.clipsToBounds = true

+ 89 - 0
AIEmoji/Common/View/UIStackView/TSCustomStackView.swift

@@ -0,0 +1,89 @@
+//
+//  CustomStackView.swift
+//  TestUIKit
+//
+//  Created by 100Years on 2025/2/24.
+//
+
+import UIKit
+import SnapKit
+
+class TSCustomStackView: UIView {
+    // 内部的 UIStackView
+    private let stackView: UIStackView
+    
+    // 开放的属性,用于设置方向和间距
+    var axis: NSLayoutConstraint.Axis {
+        get {
+            return stackView.axis
+        }
+        set {
+            stackView.axis = newValue
+        }
+    }
+    
+    var spacing: CGFloat {
+        get {
+            return stackView.spacing
+        }
+        set {
+            stackView.spacing = newValue
+        }
+    }
+    
+    // 初始化方法
+    init(axis: NSLayoutConstraint.Axis = .vertical, spacing: CGFloat = 0) {
+        self.stackView = UIStackView()
+        self.stackView.axis = axis
+        self.stackView.spacing = spacing
+        self.stackView.alignment = .fill
+        self.stackView.distribution = .fill
+        super.init(frame: .zero)
+        setupUI()
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    // 设置 UI
+    private func setupUI() {
+        addSubview(stackView)
+        stackView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+    }
+    
+    // 动态添加子视图的方法
+    func addSubviewToStack(_ view: UIView) {
+        stackView.addArrangedSubview(view)
+        // 可以根据需要对子视图进行额外的布局设置
+        view.snp.makeConstraints { make in
+            if axis == .vertical {
+                make.width.equalTo(stackView)
+            } else {
+                make.height.equalTo(stackView)
+            }
+        }
+    }
+    
+    
+    // 动态添加子视图的方法
+    func insertViewToStack(_ view: UIView, at stackIndex: Int){
+        stackView.insertArrangedSubview(view, at: 0)
+        // 可以根据需要对子视图进行额外的布局设置
+        view.snp.makeConstraints { make in
+            if axis == .vertical {
+                make.width.equalTo(stackView)
+            } else {
+                make.height.equalTo(stackView)
+            }
+        }
+    }
+        
+    func removeViewToStack(_ view: UIView){
+        stackView.removeArrangedSubview(view)
+    }
+        
+        
+}