录音详情修改
authorliangzhenying <867174124@qq.com>
Wed, 5 Nov 2025 07:13:55 +0000 (15:13 +0800)
committerliangzhenying <867174124@qq.com>
Wed, 5 Nov 2025 07:13:55 +0000 (15:13 +0800)
77 files changed:
ShenYin/ShenYin/Api/Config.swift
ShenYin/ShenYin/Api/HttpClient2.swift
ShenYin/ShenYin/Api/ResponseBaseModel.swift
ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Contents.json [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Vector@2x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Vector@3x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Contents.json [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Group_2287@2x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Group_2287@3x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Contents.json [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Frame 1938@2x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Frame 1938@3x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Contents.json [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Frame 1938@2x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Frame 1938@3x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Contents.json [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Frame@2x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Frame@3x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Contents.json [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Group_2440@2x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Group_2440@3x.png [new file with mode: 0644]
ShenYin/ShenYin/Cache/BannerCache.swift [new file with mode: 0644]
ShenYin/ShenYin/Cache/CacheManager.swift [new file with mode: 0644]
ShenYin/ShenYin/Cache/DCFolder.swift [new file with mode: 0644]
ShenYin/ShenYin/Cache/FontCache.swift [new file with mode: 0644]
ShenYin/ShenYin/Cache/RecordCache.swift [new file with mode: 0644]
ShenYin/ShenYin/Cache/RecordDetailModel.swift [new file with mode: 0644]
ShenYin/ShenYin/Cache/TipsCache.swift [new file with mode: 0644]
ShenYin/ShenYin/Cache/UserCache.swift [new file with mode: 0644]
ShenYin/ShenYin/Cache/UserInfo.swift [new file with mode: 0644]
ShenYin/ShenYin/Cache/UserModel.swift [new file with mode: 0644]
ShenYin/ShenYin/Tools/Extensions/UILabel+line.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Base/BaseNavigation.swift
ShenYin/ShenYin/UI/Base/BaseTC.swift
ShenYin/ShenYin/UI/Base/WebView/BaseWebViewController.swift
ShenYin/ShenYin/UI/Home/CallingRecordVC.swift
ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishCell.swift
ShenYin/ShenYin/UI/Home/Chinese2English/Chinese2EnglishVC.swift
ShenYin/ShenYin/UI/Home/Device/HomeDeviceVC.swift
ShenYin/ShenYin/UI/Home/Dialog/DialogTranlateVC.swift
ShenYin/ShenYin/UI/Home/FieldRecording/FieldRecordingVC.swift
ShenYin/ShenYin/UI/Home/HistoryRecord/FileSaver.swift
ShenYin/ShenYin/UI/Home/HistoryRecord/HistoryRecordCell.swift
ShenYin/ShenYin/UI/Home/HistoryRecord/RecordHistoryBottomView.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/HistoryRecord/RecordHistoryModel.swift
ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudRecordVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/HistoryRecord/VC/RecordFontVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/HistoryRecord/VC/RecordShareVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/HistoryRecord/search/HistorySearchBottomView.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/HistoryRecord/search/HistorySearchCell.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/HistoryRecord/search/HistorySelectedVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/RecordDetail/RecordDetailVC.swift
ShenYin/ShenYin/UI/Home/RecordTranslateVC.swift
ShenYin/ShenYin/UI/Home/Scan/AddDeviceVC.swift
ShenYin/ShenYin/UI/Home/Scan/BleScanVC.swift
ShenYin/ShenYin/UI/Home/TextTranslate/TranslateCell.swift
ShenYin/ShenYin/UI/Launch/AppManager.swift
ShenYin/ShenYin/UI/Launch/SceneDelegate.swift
ShenYin/ShenYin/UI/Mine/AccountSetVC.swift
ShenYin/ShenYin/UI/Mine/Feedback/Commit/FeedbackCommitVC.swift
ShenYin/ShenYin/UI/Mine/Instructions/InstructionsVC.swift
ShenYin/ShenYin/UI/Mine/Instructions/SearchBar.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/Login/ViewModel/LoginVM.swift
ShenYin/ShenYin/UI/Mine/MoreSetVC.swift
ShenYin/ShenYin/UI/Mine/RevokeVC.swift
ShenYin/ShenYin/UI/Mine/Root/BannerModel.swift
ShenYin/ShenYin/UI/Mine/Root/MineBannerView.swift
ShenYin/ShenYin/UI/Mine/Root/MineHeaderView.swift
ShenYin/ShenYin/UI/Mine/Root/MineVC.swift
ShenYin/ShenYin/UI/Mine/Update/UpdateVC.swift
ShenYin/ShenYin/UI/Mine/UserInfo/UserInfoVC.swift
ShenYin/ShenYin/UI/Mode/ModeEffectsView.swift
ShenYin/ShenYin/UI/common/BrokenLineView.swift
ShenYin/ShenYin/UI/common/Cache/DeviceCache.swift [deleted file]
ShenYin/ShenYin/UI/common/LogVC.swift
ShenYin/ShenYin/UI/common/pop/PopTextField.swift
ShenYin/ShenYin/UI/common/pop/RecordPopView.swift [new file with mode: 0644]

index 97a30a8..79d53b1 100644 (file)
@@ -11,21 +11,16 @@ class Config{
     static var appLanguage = AppSupportLanguage.chinse
 }
 class DCGlobal {
-    
-    /// 高德地图 app key
-    static let mapKey = "01d0c91a330f94bd5819d617a91d37a0"
-    
-    /// 腾讯地图 app key
-    static let qqmapKey = "FC6BZ-QCJWB-SOTUF-JLGK6-DTFIK-WCBIF"
-    /// 腾讯地图 Secret Key
-    static let qqmapSecretKey = "ODo3ZcRrsbzB71UTPBbRHeivpxnhSg9X"
+
     
     /// 微信开发平台 App ID
     static let wxAppID = "wxf19317f23b00d8ab"
     static let wxUniversalLink = "https://www.thisiot.com/flychat"
     static let wxSecret = "SECRET"
     
-    static let appStoreLink = "https://itunes.apple.com/cn/app/1542713301?mt=8"
+//    static let appStoreLink = "https://itunes.apple.com/cn/app/6738022284?mt=8"
+    static let appStoreLink = "https://itunes.apple.com/cn/app/6752551504?mt=8"
+    
     /// 版本检测
-    static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=1542713301"
+    static let appStoreVersionURL = "https://itunes.apple.com/cn/lookup?id=6752551504"
 }
index 968b41f..c8bad9f 100644 (file)
@@ -556,16 +556,17 @@ extension HttpClient{
                 return
             }
             let responseStr = response.data.string ?? ""
+            if let code = dict["code"] as? Int,code == 50000{
+                YLprogressHUD.showAutoHiden(text: "当前账号已经退出登录"){
+                    CacheManager.shared.user.logout()
+                }
+            }
             if let res = T.deserialize(from: dict){
                 completionHandler(res,responseStr)
                 return
             }
             completionHandler(T(),"请求失败")
-            if let code = dict["code"] as? Int,code == 50001{
-                YLprogressHUD.showAutoHiden(text: "当前账号已经退出登录"){
-                    CacheManager.shared.logout()
-                }
-            }
+
         }
     }
     private static func handleResponse1<T: HandyJSON>(_ data: Data?,error:Error?, completionHandler: @escaping ((T, String) -> Void)) {
@@ -590,11 +591,11 @@ extension HttpClient{
                 return
             }
             completionHandler(T(),"请求失败")
-            if let code = dict["code"] as? Int,code == 50001{
-                YLprogressHUD.showAutoHiden(text: "当前账号已经退出登录"){
-                    CacheManager.shared.logout()
-                }
-            }
+//            if let code = dict["code"] as? Int,code == 50001{
+//                YLprogressHUD.showAutoHiden(text: "当前账号已经退出登录"){
+//                    CacheManager.shared.user.logout()
+//                }
+//            }
         }
     }
     
index 74c8ebd..2265a97 100644 (file)
@@ -21,3 +21,7 @@ public class ResponseBaseModel: HandyJSON {
 class CommonDataModel:ResponseBaseModel {
     var data = ""
 }
+
+class CommonDataIntModel:ResponseBaseModel {
+    var data:Int = 0
+}
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Contents.json
new file mode 100644 (file)
index 0000000..6a4d508
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "Vector@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "Vector@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Vector@2x.png b/ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Vector@2x.png
new file mode 100644 (file)
index 0000000..afd344c
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Vector@2x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Vector@3x.png b/ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Vector@3x.png
new file mode 100644 (file)
index 0000000..ebeb78f
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/btn_normal.imageset/Vector@3x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Contents.json
new file mode 100644 (file)
index 0000000..a736381
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "Group_2287@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "Group_2287@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Group_2287@2x.png b/ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Group_2287@2x.png
new file mode 100644 (file)
index 0000000..2ee1b0b
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Group_2287@2x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Group_2287@3x.png b/ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Group_2287@3x.png
new file mode 100644 (file)
index 0000000..3633f59
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/btn_selected.imageset/Group_2287@3x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Contents.json
new file mode 100644 (file)
index 0000000..898c9dd
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "Frame 1938@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "Frame 1938@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Frame 1938@2x.png b/ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Frame 1938@2x.png
new file mode 100644 (file)
index 0000000..add52c0
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Frame 1938@2x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Frame 1938@3x.png b/ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Frame 1938@3x.png
new file mode 100644 (file)
index 0000000..52a16d1
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/delete_4.imageset/Frame 1938@3x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Contents.json
new file mode 100644 (file)
index 0000000..898c9dd
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "Frame 1938@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "Frame 1938@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Frame 1938@2x.png b/ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Frame 1938@2x.png
new file mode 100644 (file)
index 0000000..082ba7a
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Frame 1938@2x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Frame 1938@3x.png b/ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Frame 1938@3x.png
new file mode 100644 (file)
index 0000000..df7696d
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/output_icon.imageset/Frame 1938@3x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Contents.json
new file mode 100644 (file)
index 0000000..5c4d3b1
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "Frame@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "Frame@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Frame@2x.png b/ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Frame@2x.png
new file mode 100644 (file)
index 0000000..4b25d5e
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Frame@2x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Frame@3x.png b/ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Frame@3x.png
new file mode 100644 (file)
index 0000000..fec05ee
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/home/search_2.imageset/Frame@3x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Contents.json
new file mode 100644 (file)
index 0000000..fa2d8d5
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "Group_2440@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "Group_2440@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Group_2440@2x.png b/ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Group_2440@2x.png
new file mode 100644 (file)
index 0000000..2e19cf9
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Group_2440@2x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Group_2440@3x.png b/ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Group_2440@3x.png
new file mode 100644 (file)
index 0000000..ae4a26e
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/mine/update_icon.imageset/Group_2440@3x.png differ
diff --git a/ShenYin/ShenYin/Cache/BannerCache.swift b/ShenYin/ShenYin/Cache/BannerCache.swift
new file mode 100644 (file)
index 0000000..1149cc5
--- /dev/null
@@ -0,0 +1,56 @@
+//
+//  BannerCache.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/4.
+//
+
+import HandyJSON
+
+class BannerModelRoot:ResponseBaseModel {
+    var data = [BannerModel]()
+}
+
+class BannerModel:NSObject,HandyJSON,NSCoding {
+    var id = ""
+    var title = ""
+    var cover = ""
+    var url = ""
+    required public override init() { }
+    
+    func encode(with aCoder: NSCoder) {
+        aCoder.encode(id, forKey: "id")
+        aCoder.encode(title, forKey: "title")
+        aCoder.encode(cover, forKey: "cover")
+        aCoder.encode(url, forKey: "url")
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        id = aDecoder.decodeObject(forKey: "id") as? String ?? ""
+        title = aDecoder.decodeObject(forKey: "title") as? String ?? ""
+        cover = aDecoder.decodeObject(forKey: "cover") as? String ?? ""
+        url = aDecoder.decodeObject(forKey: "url") as? String ?? ""
+    }
+}
+
+
+
+class BannerCache {
+    static let shared = BannerCache()
+    private let userDefaults = UserDefaults.standard
+    private let deviceListKey = "BannerCache"
+    
+    func saveList(_ list: [BannerModel]) {
+        let encoder = NSKeyedArchiver.archivedData(withRootObject: list)
+        userDefaults.set(encoder, forKey: deviceListKey)
+        userDefaults.synchronize()
+    }
+    
+    func get() -> [BannerModel] {
+        if let data = userDefaults.data(forKey: deviceListKey),
+           let deviceList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [BannerModel] {
+            return deviceList
+        }
+        return []
+    }
+}
diff --git a/ShenYin/ShenYin/Cache/CacheManager.swift b/ShenYin/ShenYin/Cache/CacheManager.swift
new file mode 100644 (file)
index 0000000..24f4096
--- /dev/null
@@ -0,0 +1,53 @@
+//
+//  c.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/9/11.
+//
+import CoreBluetooth
+
+class CacheManager{
+    static let shared = CacheManager()
+
+    var user = UserModel()
+    
+    init(){
+        user.loadCache()
+    }
+}
+
+
+extension CacheManager{
+    static func uuid()->String{
+        if let value = UserDefaults.standard.value(forKey: "uuid"),let uuid = value as? String{
+            return uuid
+        }
+        let uuid =  UUID().uuidString
+        UserDefaults.standard.setValue(uuid, forKey: "uuid")
+        UserDefaults.standard.synchronize()
+        return uuid
+    }
+}
+
+
+extension CacheManager{
+    static func saveImg(_ img:UIImage)->String?{
+        guard let data = img.pngData() else{
+            return nil
+        }
+        
+        let filePath = DCFolder.getCacheDirectoryPath()! + "/\(Date().timeIntervalSince1970).png"
+        return saveToFile(data: data, filePath: filePath)
+    }
+    static func saveToFile(data:Data,filePath:String)->String?{
+        let url = URL(fileURLWithPath: filePath)
+        do {
+            try data.write(to: url)
+            return filePath
+        } catch {
+            Logger.debug("Error saving compressed data: \(error)")
+            return nil
+        }
+    }
+}
+
diff --git a/ShenYin/ShenYin/Cache/DCFolder.swift b/ShenYin/ShenYin/Cache/DCFolder.swift
new file mode 100644 (file)
index 0000000..fda2224
--- /dev/null
@@ -0,0 +1,66 @@
+//
+//  FileManager.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/4.
+//
+
+class DCFolder {
+    static var user:String{
+        let user = CacheManager.shared.user
+        let path = NSHomeDirectory() + "/Documents/" + user.info.id
+        createFolder(path)
+        return path
+    }
+    static var record:String{
+        let path = DCFolder.user + "/record/"
+        createFolder(path)
+        return path
+    }
+    
+    static func createFolder(_ foldPath:String){
+        Logger.debug("路径 " ,foldPath)
+        let fileManager = FileManager.default
+        if !fileManager.fileExists(atPath: foldPath) {
+            do {
+                try FileManager.default.createDirectory(atPath: foldPath, withIntermediateDirectories: true, attributes: nil)
+            } catch {
+                Logger.debug("创建文件夹失败!!!",foldPath)
+            }
+        }
+    }
+    
+    // 获取应用沙盒中的缓存目录路径
+    static func getCacheDirectoryPath() -> String? {
+        // 获取缓存目录的URL
+        if let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first {
+            // 转换为路径字符串
+            return cacheURL.path
+        }
+        return nil
+    }
+    
+    static func moveFile(srcPath:String,desPath:String)->Bool{
+        let fileManager = FileManager.default
+        do {
+            try fileManager.moveItem(atPath: srcPath, toPath: desPath)
+            return true
+        } catch {
+            Logger.debug("文件移动失败: \(error)")
+            return false
+        }
+    }
+    static func recordFileResave(srcPath:String)->String?{
+        
+        let desPath = DCFolder.record + (srcPath as NSString).lastPathComponent
+        let fileManager = FileManager.default
+        do {
+            try fileManager.moveItem(atPath: srcPath, toPath: desPath)
+            Logger.debug("文件保存路径: \(desPath)")
+            return desPath
+        } catch {
+            Logger.debug("文件移动失败: \(error)")
+            return nil
+        }
+    }
+}
diff --git a/ShenYin/ShenYin/Cache/FontCache.swift b/ShenYin/ShenYin/Cache/FontCache.swift
new file mode 100644 (file)
index 0000000..36f1c61
--- /dev/null
@@ -0,0 +1,19 @@
+//
+//  FontCache.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/24.
+//
+
+class FontCache{
+    static func saveFont(type:FontType){
+        UserDefaults.standard.set(type.rawValue, forKey: "FontCache")
+        UserDefaults.standard.synchronize()
+    }
+    static func getFont()->FontType{
+        if let value = UserDefaults.standard.value(forKey: "FontCache"),let font = value as? Int {
+            return FontType(rawValue: font) ?? .stand
+        }
+        return FontType.stand
+    }
+}
diff --git a/ShenYin/ShenYin/Cache/RecordCache.swift b/ShenYin/ShenYin/Cache/RecordCache.swift
new file mode 100644 (file)
index 0000000..0403f1a
--- /dev/null
@@ -0,0 +1,50 @@
+//
+//  RecordCache.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/24.
+//
+
+class RecordCache {
+    static let shared = RecordCache()
+    private let userDefaults = UserDefaults.standard
+    private let deviceListKey = "deviceListKey"
+    
+    func add(_ device: RecordDetailModel) {
+        var deviceList = getList()
+        if let index = deviceList.firstIndex(where: { $0.createTime == device.createTime }) {
+            deviceList[index] = device
+        }
+        else{
+            deviceList.append(device)
+        }
+        let encoder = NSKeyedArchiver.archivedData(withRootObject: deviceList)
+        userDefaults.set(encoder, forKey: deviceListKey)
+        userDefaults.synchronize()
+    }
+    func remove(_ device: RecordDetailModel) {
+        var deviceList = getList()
+        deviceList.removeAll(where: {$0.createTime == device.createTime})
+        let encoder = NSKeyedArchiver.archivedData(withRootObject: deviceList)
+        userDefaults.set(encoder, forKey: deviceListKey)
+        userDefaults.synchronize()
+    }
+    func remove(list: [RecordDetailModel]) {
+        var deviceList = getList()
+        for item in list {
+            deviceList.removeAll(where: {$0.createTime == item.createTime})
+        }
+        
+        let encoder = NSKeyedArchiver.archivedData(withRootObject: deviceList)
+        userDefaults.set(encoder, forKey: deviceListKey)
+        userDefaults.synchronize()
+    }
+    
+    func getList() -> [RecordDetailModel] {
+        if let data = userDefaults.data(forKey: deviceListKey),
+           let deviceList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [RecordDetailModel] {
+            return deviceList
+        }
+        return []
+    }
+}
diff --git a/ShenYin/ShenYin/Cache/RecordDetailModel.swift b/ShenYin/ShenYin/Cache/RecordDetailModel.swift
new file mode 100644 (file)
index 0000000..04e0f1a
--- /dev/null
@@ -0,0 +1,97 @@
+//
+//  RecordDetailModel.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/23.
+//
+import HandyJSON
+
+enum FiltterRecordType:String,HandyJSONEnum{
+    case present = "present"
+    case phone = "phone"
+    case translate = "translate"
+    case all = "all"
+    case colletion = "colletion"
+    
+    var title:String{
+        switch self {
+        case .present:
+            return "现场录音"
+        case .phone:
+            return "通话录音"
+        case .translate:
+            return "同传听译"
+        case .all:
+            return "全部录音"
+        case .colletion:
+            return "收藏录音"
+        }
+    }
+    static var alls:[FiltterRecordType]{
+        return [.all,.colletion,.present,.phone,.translate]
+    }
+}
+
+enum RecordType:String,HandyJSONEnum{
+    case present = "present"
+    case phone = "phone"
+    case translate = "translate"
+    
+    var title:String{
+        switch self {
+        case .present:
+            return "现场录音"
+        case .phone:
+            return "通话录音"
+        case .translate:
+            return "同传听译"
+        }
+    }
+}
+
+class RecordDetailModel:NSObject, NSCoding{
+    func encode(with coder: NSCoder) {
+        coder.encode(from.rawValue, forKey: "from")
+        coder.encode(to.rawValue, forKey: "to")
+        coder.encode(type.rawValue, forKey: "type")
+        coder.encode(origin, forKey: "origin")
+        coder.encode(translate, forKey: "translate")
+        coder.encode(summary, forKey: "summary")
+        coder.encode(path, forKey: "path")
+        coder.encode(name, forKey: "name")
+        coder.encode("\(duration)", forKey: "duration")
+        coder.encode(createTime, forKey: "createTime")
+        coder.encode(favorite, forKey: "favorite")
+    }
+    override init() {
+        createTime = Date().format(.full)
+    }
+    
+    required init?(coder: NSCoder) {
+        from = AiSupportLanguage(rawValue: coder.decodeObject(forKey: "from") as! String) ?? .chinse
+        to = AiSupportLanguage(rawValue: coder.decodeObject(forKey: "to") as! String) ?? .english
+        type = RecordType(rawValue: coder.decodeObject(forKey: "type") as! String) ?? .present
+        origin = coder.decodeObject(forKey: "origin") as? String ?? ""
+        translate = coder.decodeObject(forKey: "translate") as? String ?? ""
+        summary = coder.decodeObject(forKey: "summary") as? String ?? ""
+        path = coder.decodeObject(forKey: "path") as? String ?? ""
+        name = coder.decodeObject(forKey: "name") as? String ?? ""
+        duration = Int(coder.decodeObject(forKey: "duration") as! String) ?? 0
+        createTime = coder.decodeObject(forKey: "createTime") as? String ?? ""
+        favorite = coder.decodeObject(forKey: "favorite") as? String ?? ""
+    }
+    var type:RecordType = .present
+    var from:AiSupportLanguage = .chinse
+    var to:AiSupportLanguage = .english
+    var path = ""
+    var name = ""
+    var duration = 0
+    var createTime = ""
+    var origin = ""
+    
+    var translate = ""
+    var summary = ""
+    var favorite = "0"
+    
+
+}
diff --git a/ShenYin/ShenYin/Cache/TipsCache.swift b/ShenYin/ShenYin/Cache/TipsCache.swift
new file mode 100644 (file)
index 0000000..d2f1b3a
--- /dev/null
@@ -0,0 +1,19 @@
+//
+//  TipsCache.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/9/26.
+//
+
+class TipsCache{
+    static func needShow(type:RecordTipsType,show:Bool){
+        UserDefaults.standard.set(show, forKey: type.title)
+        UserDefaults.standard.synchronize()
+    }
+    static func needShow(type:RecordTipsType)->Bool{
+        if let value = UserDefaults.standard.value(forKey: type.title){
+            return value as? Bool ?? false
+        }
+        return true
+    }
+}
diff --git a/ShenYin/ShenYin/Cache/UserCache.swift b/ShenYin/ShenYin/Cache/UserCache.swift
new file mode 100644 (file)
index 0000000..0520d0e
--- /dev/null
@@ -0,0 +1,95 @@
+//
+//  UserCache.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/5.
+//
+
+import HandyJSON
+
+class UserCacheInfo:NSObject,HandyJSON,NSCoding {
+    var id = ""
+    var avatar = ""
+    var userName = ""
+    var createTime = ""
+    var phone = ""
+    var email = ""
+    var gender = GenderType.unnkown
+    var countryName = ""
+    var countryId = ""
+    
+    required public override init() { }
+    
+    func encode(with aCoder: NSCoder) {
+        aCoder.encode(id, forKey: "id")
+        aCoder.encode(avatar, forKey: "avatar")
+        aCoder.encode(userName, forKey: "userName")
+        aCoder.encode(createTime, forKey: "createTime")
+        aCoder.encode(phone, forKey: "phone")
+        aCoder.encode(email, forKey: "email")
+        aCoder.encode(gender.rawValue, forKey: "gender")
+        aCoder.encode(countryName, forKey: "countryName")
+        aCoder.encode(countryId, forKey: "countryId")
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        id = aDecoder.decodeObject(forKey: "id") as? String ?? ""
+        avatar = aDecoder.decodeObject(forKey: "avatar") as? String ?? ""
+        userName = aDecoder.decodeObject(forKey: "userName") as? String ?? ""
+        createTime = aDecoder.decodeObject(forKey: "createTime") as? String ?? ""
+        phone = aDecoder.decodeObject(forKey: "phone") as? String ?? ""
+        email = aDecoder.decodeObject(forKey: "email") as? String ?? ""
+        gender = GenderType(rawValue: aDecoder.decodeObject(forKey: "gender") as! String) ?? .unnkown
+        countryName = aDecoder.decodeObject(forKey: "countryName") as? String ?? ""
+        countryId = aDecoder.decodeObject(forKey: "countryId") as? String ?? ""
+    }
+    static func cacheToInfo(_ cache:UserCacheInfo)->UserInfo{
+        let info = UserInfo()
+        info.id = cache.id
+        info.avatar = cache.avatar
+        info.userName = cache.userName
+        info.createTime = cache.createTime
+        info.phone = cache.phone
+        info.email = cache.email
+        info.gender = cache.gender
+        info.countryName = cache.countryName
+        info.countryId = cache.countryId
+        return info
+    }
+    static func infoToCache(_ info:UserInfo)->UserCacheInfo{
+        let cache = UserCacheInfo()
+        cache.id = info.id
+        cache.avatar = info.avatar
+        cache.userName = info.userName
+        cache.createTime = info.createTime
+        cache.phone = info.phone
+        cache.email = info.email
+        cache.gender = info.gender
+        cache.countryName = info.countryName
+        cache.countryId = info.countryId
+        return cache
+    }
+}
+
+
+class UserCache {
+    static let shared = UserCache()
+    private let userDefaults = UserDefaults.standard
+    private let key = "UserCacheInfo"
+    
+    func save(_ info: UserInfo) {
+        let cache =  UserCacheInfo.infoToCache(info)
+        let encoder = NSKeyedArchiver.archivedData(withRootObject: cache)
+        userDefaults.set(encoder, forKey: key)
+        userDefaults.synchronize()
+    }
+    
+    func get() -> UserInfo? {
+        if let data = userDefaults.data(forKey: key),
+            let model = NSKeyedUnarchiver.unarchiveObject(with: data) as? UserCacheInfo {
+            let info =  UserCacheInfo.cacheToInfo(model)
+            return info
+        }
+        return nil
+    }
+}
diff --git a/ShenYin/ShenYin/Cache/UserInfo.swift b/ShenYin/ShenYin/Cache/UserInfo.swift
new file mode 100644 (file)
index 0000000..bd069f8
--- /dev/null
@@ -0,0 +1,43 @@
+//
+//  Untitled.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/9/29.
+//
+import HandyJSON
+
+enum GenderType:String,HandyJSONEnum{
+    case man = "1"
+    case female = "0"
+    case unnkown = "2"
+    var title:String{
+        switch self {
+        case .man:
+            return "男"
+        case .female:
+            return "女"
+        case .unnkown:
+            return "保密"
+        }
+    }
+}
+
+
+class UserInfoRoot:ResponseBaseModel {
+    var data = UserInfo()
+}
+
+class UserInfo:ResponseBaseModel {
+    var id = ""
+    var avatar = ""
+    var userName = ""
+    var createTime = ""
+    var phone = ""
+    var email = ""
+    var gender = GenderType.unnkown
+    var countryName = ""
+    var countryId = ""
+}
+
+
+
diff --git a/ShenYin/ShenYin/Cache/UserModel.swift b/ShenYin/ShenYin/Cache/UserModel.swift
new file mode 100644 (file)
index 0000000..c764725
--- /dev/null
@@ -0,0 +1,89 @@
+//
+//  UserModel.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/7/30.
+//
+
+
+class UserModel {
+    static let cacheKey = "Login"
+    var token:String? {
+        didSet{
+           saveToken(text: token)
+        }
+    }
+    var isLogin:Bool{
+        return token != nil
+    }
+    func loadCache(){
+        self.token = getToken()
+        if let cache = UserCache.shared.get(){
+            self.info = cache
+        }
+        
+        if self.token != nil {
+            tokenIsValid()
+        }
+    }
+    
+    var storageSize = 0
+    var info = UserInfo()
+}
+
+extension UserModel{
+    func login(token:String){
+        self.token = token
+        NotificationManager.post(type: .login, objtect: nil)
+        
+    }
+    func logout(){
+        self.token = nil
+        NotificationManager.post(type: .logout, objtect: nil)
+        App.shared.goToLogin()
+    }
+    func tokenIsValid(finished: (() -> Void)? = nil){
+        HttpClient.request(CommomApi.token) { [weak self](model:UserInfoRoot,res) in
+            guard let self = self else{return}
+            if model.isSuccess{
+                self.info = model.data
+                UserCache.shared.save(self.info)
+            }
+//            else{
+//                logout()
+//            }
+            finished?()
+        }
+    }
+    
+    func getStorageSize(completion:@escaping (_ result:CommonDataIntModel) ->Void){
+        HttpClient.request(.storageSize) { [weak self](result:CommonDataIntModel,res) in
+            guard let self = self else{return}
+            if result.isSuccess{
+                storageSize = result.data
+            }
+            completion(result)
+        }
+    }
+
+}
+
+extension UserModel{
+     private func getToken()->String?{
+         if let value = UserDefaults.standard.value(forKey: UserModel.cacheKey),let token = value as? String{
+            return token
+        }
+        return nil
+
+    }
+    private func saveToken(text:String?){
+        if let text = text {
+            UserDefaults.standard.set(text, forKey: UserModel.cacheKey)
+            UserDefaults.standard.synchronize()
+        }
+        else{
+            UserDefaults.standard.removeObject(forKey: UserModel.cacheKey)
+            UserDefaults.standard.synchronize()
+        }
+    }
+}
diff --git a/ShenYin/ShenYin/Tools/Extensions/UILabel+line.swift b/ShenYin/ShenYin/Tools/Extensions/UILabel+line.swift
new file mode 100644 (file)
index 0000000..69a8d2f
--- /dev/null
@@ -0,0 +1,18 @@
+//
+//  UILabel+line.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/3.
+//
+import UIKit
+
+extension UILabel {
+    func setLineSpacing(spacing: CGFloat) {
+        guard let text = text else { return }
+        let attributedString = NSMutableAttributedString(string: text)
+        let paragraphStyle = NSMutableParagraphStyle()
+        paragraphStyle.lineSpacing = spacing
+        attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
+        self.attributedText = attributedString
+    }
+}
index 0b1e1bf..1bdb409 100644 (file)
@@ -49,7 +49,7 @@ public class BaseNavigationBar: UIView {
     public let leftView = UIButton()
     public let titleLabel = UILabel()
     public let separatorLine = UIView()
-    public lazy var rightView = setupRightView()
+    public var rightView = UIButton()
     
     private var navTitle: String
     public init(title: String = "", delegate: BaseNavigationBarDelegate? = nil) {
@@ -147,23 +147,23 @@ public class BaseNavigationBar: UIView {
             v.setImage(UIImage(named: "arrow_back"), for: .normal)
             v.addTarget(self, action: #selector(leftViewTapped), for: .touchUpInside)
         }
+        setupRightView()
     }
     
-    func setupRightView() -> UIButton {
-        let rightBtn = UIButton()
-        addSubview(rightBtn)
-        rightBtn.snp.makeConstraints { (m) in
+    func setupRightView() {
+        rightView.isHidden = true
+        addSubview(rightView)
+        rightView.snp.makeConstraints { (m) in
             m.top.equalTo(kNavTop)
             m.trailing.bottom.equalToSuperview()
             m.width.greaterThanOrEqualTo(48)
         }
-        rightBtn.do { (v) in
+        rightView.do { (v) in
             v.setTitleColor(.rgb(0x222222), for: .normal)
             v.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 15)
             v.imageView?.contentMode = .right
             v.addTarget(self, action: #selector(rightViewTapped), for: .touchUpInside)
         }
-        return rightBtn
     }
 
     @discardableResult
@@ -198,19 +198,20 @@ public class BaseNavigationBar: UIView {
         }
         return views
     }
-//    func setupRightViews(_ views:[UIView]){
-//        var x = 0
-//        for item in views {
-//            let w = item.bounds.size.width
-//            addSubview(item)
-//            item.snp.makeConstraints { (m) in
-//                m.top.equalTo(kNavTop)
-//                m.bottom.equalToSuperview()
-//                m.width.greaterThanOrEqualTo(w)
-//                m.right.equalToSuperview().inset(x)
-//            }
-//
-//            x = x + w
-//        }
-//    }
+    func setupRightViews(_ views:[UIView]){
+        var x = 0.0
+        for item in views {
+            let w = item.bounds.size.width
+            addSubview(item)
+            item.snp.makeConstraints { (m) in
+                m.top.equalTo(kNavTop)
+                m.bottom.equalToSuperview()
+                m.width.greaterThanOrEqualTo(w)
+                m.right.equalToSuperview().inset(x)
+            }
+
+            x = x + w
+        }
+    }
+
 }
index 1f0a66a..979cc38 100644 (file)
@@ -55,6 +55,6 @@ class BaseTC:UITabBarController{
         self.tabBar.backgroundColor = .white
         view.backgroundColor = .color(.bgColor)
 
-        self.selectedIndex = 0
+//        self.selectedIndex = 4
     }
 }
index 60f1899..d7a8f3e 100644 (file)
@@ -39,6 +39,7 @@ final class BaseWebViewController: BaseVC {
         webView.addSubview(progressView)
         
         navBar.title = initData.title
+        navBar.rightView.isHidden = false
         navBar.rightView.setTitle("关闭", for: .normal)
         
         view.backgroundColor = .color(.bgColor)
index bc3b725..627dd66 100644 (file)
@@ -117,7 +117,7 @@ extension CallingRecordVC:FieldRecordBarDelegate{
             bar.startAnimating {[weak self] in
                 guard let self = self else{return}
                 //toolBar.recordBtn.stopCli()
-                guard let path = recordFilePath else{return}
+                guard let path = recordFilePath,let wavPath = DCFolder.recordFileResave(srcPath: path) else{return}
                 let v = PopTextField()
                 v.show()
                 v.cancelTap = {
@@ -131,7 +131,7 @@ extension CallingRecordVC:FieldRecordBarDelegate{
                     model.from = bar.leftItem.language
                     model.to = .english
                     model.name = text
-                    model.path = path
+                    model.path = wavPath
                     model.duration = YLToos.getDuration(model.path)
                     model.origin = textView.text
                     model.translate = ""
index 69b31f3..3a5d655 100644 (file)
@@ -54,7 +54,7 @@ class Chinese2EnglishCell:BaseTableViewCell{
             v.textAlignment = .left
         }
         line.do { v in
-            //            v.backgroundColor = .hexString("#93C6EC")
+//                        v.backgroundColor = .hexString("#93C6EC")
         }
         copyIcon.do { v in
             v.image = UIImage(named: "play_animate_3")
index 5ddb382..dbae000 100644 (file)
@@ -18,6 +18,7 @@ class Chinese2EnglishVC:BaseVC{
 
     override func setupViews() {
         navBar.title = "中英互译"
+        navBar.rightView.isHidden = false
         navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal)
         view.addSubview(tableView)
         view.addSubview(toolBar)
@@ -127,7 +128,7 @@ extension Chinese2EnglishVC:RecordBarDelegate{
             bar.startAnimating {[weak self] in
                 guard let self = self else{return}
                 //                toolBar.recordBtn.stopCli()
-                guard let path = recordFilePath else{return}
+                guard let path = recordFilePath,let wavPath = DCFolder.recordFileResave(srcPath: path) else{return}
                 let v = PopTextField()
                 v.show()
                 v.cancelTap = {
@@ -148,7 +149,7 @@ extension Chinese2EnglishVC:RecordBarDelegate{
                     model.from = bar.leftItem.language
                     model.to = bar.rightItem.language
                     model.name = text
-                    model.path = path
+                    model.path = wavPath
                     model.duration = YLToos.getDuration(model.path)
                     model.origin = origin
                     model.translate = translate
index abf43d6..585beb2 100644 (file)
@@ -21,6 +21,7 @@ class HomeDeviceVC:BaseVC{
     
     override func setupViews() {
         navBar.leftView.isHidden = true
+        navBar.rightView.isHidden = false
         navBar.rightView.setImage(UIImage(named: "add_icon"), for: .normal)
 
         
@@ -54,7 +55,7 @@ class HomeDeviceVC:BaseVC{
                 self?.startConectBle(model)
             }
         }
-        CacheManager.shared.tokenIsValid(){[weak self] in
+        CacheManager.shared.user.tokenIsValid(){[weak self] in
             self?.collectionView.reloadData()
         }
     }
@@ -66,6 +67,7 @@ class HomeDeviceVC:BaseVC{
         //        v.show(animate: true)
         
         App.shared.goToPage(BleScanVC())
+
     }
     
 
index e026732..dcaf819 100644 (file)
@@ -27,6 +27,7 @@ class DialogTranslateVC:BaseVC{
         view.addSubviews([toolBar,tableView,splitView])
         
         navBar.title = "对话翻译"
+        navBar.rightView.isHidden = false
         navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal)
         
         toolBar.do { v in
index c1be8b8..09d60b3 100644 (file)
@@ -115,7 +115,7 @@ extension FieldRecordingVC:FieldRecordBarDelegate{
             bar.startAnimating {[weak self] in
                 guard let self = self else{return}
 //                toolBar.recordBtn.stopCli()
-                guard let path = recordFilePath else{return}
+                guard let path = recordFilePath,let wavPath = DCFolder.recordFileResave(srcPath: path) else{return}
                 let v = PopTextField()
                 v.show()
                 v.cancelTap = {
@@ -129,7 +129,7 @@ extension FieldRecordingVC:FieldRecordBarDelegate{
                     model.from = bar.leftItem.language
                     model.to = .english
                     model.name = text
-                    model.path = path
+                    model.path = wavPath
                     model.duration = YLToos.getDuration(model.path)
                     model.origin = textView.text
                     model.translate = ""
index 8585661..a866a7c 100644 (file)
@@ -19,8 +19,8 @@ class FileSaver {
     ///   - string: 要保存的字符串
     ///   - fileName: 文件名(不包含扩展名)
     /// - Returns: 是否保存成功
-    static func saveStringAsTxt(_ string: String) -> String? {
-        let fileURL = getDocumentsDirectory().appendingPathComponent("\(Date().format(.full6))").appendingPathExtension("txt")
+    static func saveStringAsTxt(_ string: String,fileName:String = "\(Date().format(.full6))") -> String? {
+        let fileURL = getDocumentsDirectory().appendingPathComponent(fileName).appendingPathExtension("txt")
         do {
             try string.write(to: fileURL, atomically: true, encoding:.utf8)
             return fileURL.path
@@ -29,6 +29,7 @@ class FileSaver {
             return nil
         }
     }
+
 }
 
 
index 7c84b32..6f4090e 100644 (file)
@@ -14,6 +14,7 @@ class HistoryRecordCell: BaseTableViewCell {
     let durationLabel = UILabel()
     let dateLabel = UILabel()
     let typeLabel = UILabel()
+    var longPressCallback:((_ cell:HistoryRecordCell)->Void)?
     
     var model:RecordHistoryModel?{
         didSet{
@@ -56,6 +57,8 @@ class HistoryRecordCell: BaseTableViewCell {
             v.backgroundColor = UIColor.white
             v.layer.cornerRadius = 12
             v.layer.masksToBounds = true
+            let longTap = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:)))
+            bgView.addGestureRecognizer(longTap)
         }
         titleLabel.do { (v) in
             v.textColor = .rgb(0x121212)
@@ -87,6 +90,7 @@ class HistoryRecordCell: BaseTableViewCell {
         playIcon.do { v in
             v.image = UIImage(named: "record_play 2")
         }
+        
     }
     
     override func setupLayout() {
@@ -105,6 +109,7 @@ class HistoryRecordCell: BaseTableViewCell {
         titleLabel.snp.makeConstraints { (m) in
             m.top.equalTo(16)
             m.left.equalTo(playIcon.snp.right).offset(6)
+            m.right.lessThanOrEqualTo(typeLabel.snp.left).offset(-50)
         }
         collectionIcon.snp.makeConstraints { (m) in
             m.size.equalTo(16)
@@ -143,4 +148,34 @@ class HistoryRecordCell: BaseTableViewCell {
             RecordCache.shared.add(localModel)
         }
     }
+    
+    @objc func longPressAction(_ gesture: UILongPressGestureRecognizer) {
+        if gesture.state == .began {
+            bgView.backgroundColor = .lightGray.withAlphaComponent(0.5)
+            longPressCallback?(self)
+        } else if gesture.state == .ended {
+            print("长按结束")
+        }
+    }
+
+    override func setEditing(_ editing: Bool, animated: Bool) {
+        super.setEditing(editing, animated: animated)
+        if editing {
+            if let deleteButton = findDeleteButton(in: self) {
+                deleteButton.backgroundColor = UIColor.red
+                deleteButton.setTitleColor(UIColor.white, for:.normal)
+            }
+        }
+    }
+    
+    private func findDeleteButton(in view: UIView) -> UIButton? {
+        for subview in view.subviews {
+            if let button = subview as? UIButton, button.titleLabel?.text == "自定义删除" {
+                return button
+            } else if let foundButton = findDeleteButton(in: subview) {
+                return foundButton
+            }
+        }
+        return nil
+    }
 }
diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/RecordHistoryBottomView.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/RecordHistoryBottomView.swift
new file mode 100644 (file)
index 0000000..27c329e
--- /dev/null
@@ -0,0 +1,7 @@
+//
+//  RecordHistoryBottomView.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/4.
+//
+
index 2a96720..a41484e 100644 (file)
@@ -41,3 +41,10 @@ class RecordHistoryModel:HandyJSON{
     var updateTime = ""
     required public init() { }
 }
+
+class RecordHistorySelectedModel:HandyJSON{
+    var isSelcted = false
+    var model = RecordDetailModel()
+
+    required public init() { }
+}
diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudRecordVC.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/CloudRecordVC.swift
new file mode 100644 (file)
index 0000000..ebb8676
--- /dev/null
@@ -0,0 +1,142 @@
+//
+//  CloudRecordVC.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/25.
+//
+
+
+import UIKit
+import MJRefresh
+class CloudRecordVC: BaseVC{
+    let tableView = UITableView(frame: .zero, style: .plain)
+    let vm = HistoryRecordVM()
+    var list = [RecordHistoryModel]()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        // Do any additional setup after loading the view.
+    }
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        tableView.reloadData()
+    }
+    override func setupViews() {
+        navBar.do { v in
+            v.title = "云存储空间"
+            let items = [NavigationRightItem(image: UIImage(named: "filter"),target: self, action: #selector(filterAction)),NavigationRightItem(image: UIImage(named: "search"), target: self,action: #selector(searchAction))]
+            v.setupRightViews(items)
+        }
+        
+        tableView.do { v in
+            v.backgroundColor = .rgb(0xF7F7F7)
+            v.delegate = self
+            v.dataSource = self
+            v.estimatedRowHeight = 84
+            v.separatorStyle = .none
+            v.register(HistoryRecordCell.self, forCellReuseIdentifier: HistoryRecordCell.reuseID)
+            v.mj_header = MJRefreshNormalHeader(refreshingTarget: self, refreshingAction: #selector(refreshAction))
+            v.mj_footer = MJRefreshBackNormalFooter(refreshingTarget: self, refreshingAction: #selector(loadMoreAction))
+        }
+        view.addSubviews([tableView])
+    }
+    override func setupLayout() {
+        tableView.snp.makeConstraints { make in
+            make.left.right.equalToSuperview()
+            make.top.equalTo(kNavBtm)
+            make.bottom.equalToSuperview()
+        }
+    }
+    override func setupData() {
+        refreshAction()
+    }
+    @objc func searchAction(){
+        
+    }
+    @objc func filterAction(){
+        let list = FiltterRecordType.alls.map({$0.title})
+        let v = CustomPicker(title: "筛选录音", list:list )
+        v.selectedIndex = list.firstIndex(where: {vm.type.title == $0})!
+        v.show()
+        v.didSelected = {[weak self] (index,value) in
+            guard let self = self else { return  }
+            vm.type = FiltterRecordType.alls.first(where: {$0.title == value})!
+            tableView.mj_header?.beginRefreshing()
+        }
+    }
+    @objc func refreshAction(){
+        vm.refresh {[weak self] result in
+            guard let self = self else {return}
+            tableView.mj_header?.endRefreshing()
+            if result.success{
+                tableView.reloadData()
+            }
+            
+            if result.success,result.data.hasNext == false{
+                tableView.mj_footer?.endRefreshingWithNoMoreData()
+            }
+            else{
+                tableView.mj_footer?.endRefreshing()
+            }
+        }
+    }
+    @objc func loadMoreAction(){
+        vm.loadMore {[weak self] result in
+            guard let self = self else {return}
+            if result.success{
+                tableView.reloadData()
+            }
+        }
+    }
+}
+
+extension CloudRecordVC:UITableViewDelegate,UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return vm.list.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = HistoryRecordCell.cell(withTableView: tableView, indexPath: indexPath)
+        cell.model = vm.list[indexPath.row]
+        return cell
+    }
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let data = vm.list[indexPath.row]
+        let vc = RecordDetailVC(remoteData: data)
+        vc.delegateCallback = {[weak self] in
+            guard let self = self else{return}
+            vm.list.removeAll(where: {data === $0})
+            tableView.reloadData()
+        }
+        App.shared.goToPage(vc)
+//        let api = CommomApi.getVoiceRecord(recordId: data.id)
+//        HttpClient.request(api) {[weak self] (result:RecordHistoryDetailRoot,res) in
+//            guard let self = self else {return}
+//            if result.isSuccess{
+//                let model = RecordDetailModel()
+//                model.type = .translate
+//                model.origin = result.data.summary
+//                model.translate = ""
+//                model.path = result.data.fileId
+//                model.name = result.data.name
+//                App.shared.goToPage(RecordDetailVC(record: model))
+//            }
+//        }
+        
+        
+    }
+    // 实现滑动删除功能
+    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
+        if editingStyle == .delete {
+            // 从数据源中移除对应数据
+            vm.list.remove(at: indexPath.row)
+            // 从表格中删除对应行
+            tableView.deleteRows(at: [indexPath], with:.fade)
+        }
+    }
+    
+    // 允许编辑
+    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
+        return true
+    }
+}
diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/RecordFontVC.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/RecordFontVC.swift
new file mode 100644 (file)
index 0000000..af5ac80
--- /dev/null
@@ -0,0 +1,80 @@
+//
+//  RecordFontVC.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/8/13.
+//
+
+import UIKit
+class RecordFontVC: BaseVC{
+    let tableView = UITableView(frame: .zero, style: .plain)
+    var list:[FontType] = [.min,.stand,.big]
+    var currentType = FontCache.getFont()
+    let btn = CustomButton()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        // Do any additional setup after loading the view.
+    }
+    override func setupViews() {
+        self.navBar.title = "调整字号"
+        tableView.do { v in
+            v.backgroundColor = .rgb(0xF7F7F7)
+            v.delegate = self
+            v.dataSource = self
+            v.rowHeight = 52
+            v.separatorStyle = .none
+            v.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0)
+            v.register(SelectCell.self, forCellReuseIdentifier: SelectCell.reuseID)
+        }
+        view.addSubviews([tableView,btn])
+        
+        btn.set(title: "设置", target: self, action: #selector(btnAction))
+    }
+    override func setupLayout() {
+        tableView.snp.makeConstraints { make in
+            make.left.right.bottom.equalToSuperview()
+            make.top.equalTo(kNavBtm)
+        }
+        btn.snp.makeConstraints { make in
+            make.width.equalTo(272)
+            make.height.equalTo(46)
+            make.centerX.equalToSuperview()
+            make.bottom.equalToSuperview().inset(82)
+        }
+    }
+    @objc func btnAction(){
+        FontCache.saveFont(type: currentType)
+        self.navigationController?.popViewController(animated: true)
+    }
+}
+
+extension RecordFontVC:UITableViewDelegate,UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return list.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = SelectCell.cell(withTableView: tableView, indexPath: indexPath)
+        cell.fontType = list[indexPath.row]
+        let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
+        if indexPath.row == 0 {
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 8, height: 8))
+        }
+        else if indexPath.row == list.count - 1{
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 8, height: 8))
+        }
+        else{
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0))
+        }
+        cell.selectedIcon.isHidden = cell.fontType != currentType
+        return cell
+    }
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let data = list[indexPath.row]
+        currentType = data
+        tableView.reloadData()
+    }
+}
+
+
diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/RecordShareVC.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/VC/RecordShareVC.swift
new file mode 100644 (file)
index 0000000..b21359f
--- /dev/null
@@ -0,0 +1,189 @@
+//
+//  RecordShareVC.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/8/13.
+//
+
+import UIKit
+class RecordShareVC: BaseVC{
+    let tableView = UITableView(frame: .zero, style: .plain)
+    var list:[ShareType] = [.audio_document,.audio,.document]
+    var currentType = ShareType.audio_document
+    let btn = CustomButton()
+    
+    var recordList = [RecordDetailModel]()
+    var cloudList = [RecordHistoryModel]()
+    
+    init(localList: [RecordDetailModel]) {
+        self.recordList = localList
+        super.init()
+    }
+    init(cloudList : [RecordHistoryModel]) {
+        self.cloudList = cloudList
+        super.init()
+    }
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        // Do any additional setup after loading the view.
+    }
+    override func setupViews() {
+        self.navBar.title = "分享"
+        tableView.do { v in
+            v.backgroundColor = .rgb(0xF7F7F7)
+            v.delegate = self
+            v.dataSource = self
+            v.rowHeight = 52
+            v.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0)
+            v.separatorStyle = .none
+            v.register(SelectCell.self, forCellReuseIdentifier: SelectCell.reuseID)
+            v.tableHeaderView = ShareHeaderView(frame: CGRect(x: 0, y: 0, width: kScreenWid, height: 70))
+        }
+        view.addSubviews([tableView,btn])
+        
+        btn.set(title: "导出", target: self, action: #selector(btnAction))
+    }
+    override func setupLayout() {
+        tableView.snp.makeConstraints { make in
+            make.left.right.bottom.equalToSuperview()
+            make.top.equalTo(kNavBtm)
+        }
+        btn.snp.makeConstraints { make in
+            make.width.equalTo(272)
+            make.height.equalTo(46)
+            make.centerX.equalToSuperview()
+            make.bottom.equalToSuperview().inset(82)
+        }
+    }
+    
+    override func setupData() {
+
+    }
+    func loadFile(filedId id:String){
+        let api = CommomApi.recordUrl(filedId: id)
+        HttpClient.request(api) {[weak self] (model:CommonDataModel,res) in
+            guard let self = self else {return}
+            if model.isSuccess{
+                startDownload(url: model.data)
+            }
+        }
+    }
+    func startDownload(url:String){
+        //        let url = "https://dc-cloud-priv.tos-cn-guangzhou.volces.com/music-file/2025_10_09/1976135290430767105.mp3"
+//        HttpClient.download(url: url,type: "wav"){[weak self] result in
+//            guard let self = self else {return}
+//            if let path = result{
+//                recordModel.path = path
+//            }
+//        }
+    }
+    
+    @objc func btnAction(){
+        if recordList.count == 1,let recordModel = recordList.first {
+            if currentType == .audio {
+                if recordModel.path.isNotEmpty {
+                    saveFile(filePath: recordModel.path)
+                }
+            }
+            else if currentType == .document{
+                guard let path = FileSaver.saveStringAsTxt(recordModel.origin,fileName: recordModel.name + "") else {
+                    return
+                }
+                saveFile(filePath: path)
+            }
+            else{
+                
+                guard let orginPath = FileSaver.saveStringAsTxt(recordModel.origin ,fileName: recordModel.name + "") else {
+                    return
+                }
+                var list = [orginPath]
+                if recordModel.path.isNotEmpty {
+                    list.append(recordModel.path)
+                }
+                
+                if recordModel.translate.count > 0,let path = FileSaver.saveStringAsTxt(recordModel.translate ,fileName: recordModel.name + "_translate") {
+                    list.append(path)
+                }
+                if recordModel.summary.count > 0,let path = FileSaver.saveStringAsTxt(recordModel.summary ,fileName: recordModel.name + "_summary") {
+                    list.append(path)
+                }
+                guard let path = FileSaver.zipFiles(files: list) else { return  }
+                
+                saveFile(filePath: path)
+            }
+        }
+    }
+}
+extension RecordShareVC:UIDocumentPickerDelegate{
+    //保存文件到手机文件指定目录
+    func saveFile(filePath:String) {
+        if !FileManager.default.fileExists(atPath: filePath){
+            Logger.debug("文件不存在",filePath)
+            return
+        }
+        
+        let fUrl = NSURL(fileURLWithPath: filePath)
+        let vc = UIDocumentPickerViewController(url: fUrl as URL, in: .exportToService);
+        vc.delegate = self
+        vc.modalPresentationStyle = .overFullScreen
+        self.present(vc, animated: true)
+    }
+    public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
+        controller.dismiss(animated: true)
+    }
+}
+
+extension RecordShareVC:UITableViewDelegate,UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return list.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = SelectCell.cell(withTableView: tableView, indexPath: indexPath)
+        cell.shareType = list[indexPath.row]
+        let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
+        if indexPath.row == 0 {
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 8, height: 8))
+        }
+        else if indexPath.row == list.count - 1{
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 8, height: 8))
+        }
+        else{
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0))
+        }
+        cell.selectedIcon.isHidden = cell.shareType != currentType
+        return cell
+    }
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let data = list[indexPath.row]
+        currentType = data
+        tableView.reloadData()
+    }
+}
+
+class ShareHeaderView: BaseView {
+    let titleLabel = UILabel()
+
+    override func setupViews() {
+        addSubviews([titleLabel])
+        titleLabel.do { (v) in
+            v.textColor = .hexString("#666666")
+            v.font = .regular(14)
+            v.numberOfLines = 0
+
+            let text = "多个文件将压缩成一个.zip的文件以便于分享。请选择您需要导出的文件类型:"
+            let textString = NSAttributedString(string: text)
+            let mutableAttributedString = NSMutableAttributedString()
+            mutableAttributedString.append(textString)
+            v.attributedText = mutableAttributedString
+        }
+    }
+    
+    override func setupLayout() {
+        titleLabel.snp.makeConstraints { make in
+            make.left.right.equalToSuperview().inset(18)
+            make.top.equalToSuperview()
+            make.bottom.equalToSuperview()
+        }
+    }
+}
diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/search/HistorySearchBottomView.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/search/HistorySearchBottomView.swift
new file mode 100644 (file)
index 0000000..491c402
--- /dev/null
@@ -0,0 +1,66 @@
+//
+//  HistorySearchBottomView.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/4.
+//
+
+
+class HistorySearchBottomView: BaseView {
+     let outItem = BarItem()
+     let deleteItem = BarItem()
+    
+    override func setupViews() {
+        backgroundColor = .white
+        addSubviews([outItem,deleteItem])
+        outItem.do { v in
+            v.icon.image = UIImage(named: "output_icon")
+            v.titleLabel.text = "导出"
+        }
+        deleteItem.do { v in
+            v.icon.image = UIImage(named: "delete_4")
+            v.titleLabel.text = "删除"
+        }
+    }
+    override func setupLayout() {
+        outItem.snp.makeConstraints { make in
+            make.size.equalTo(80)
+            make.top.equalTo(15)
+            make.centerX.equalTo(kScreenWid/4)
+        }
+        deleteItem.snp.makeConstraints { make in
+            make.size.equalTo(80)
+            make.top.equalTo(15)
+            make.centerX.equalTo(kScreenWid/4*3)
+        }
+    }
+    
+     class BarItem: BaseView {
+        let icon = UIImageView()
+        let titleLabel = UILabel()
+
+        override func setupViews() {
+//            backgroundColor = .red
+            addSubviews([titleLabel,icon])
+            icon.do { v in
+                v.image = UIImage(named: "polygon")
+            }
+            titleLabel.do { v in
+                v.textColor = .rgb(0x121212)
+                v.font = .regular(10)
+            }
+        }
+        
+        override func setupLayout() {
+            icon.snp.makeConstraints { make in
+                make.size.equalTo(30)
+                make.top.equalToSuperview()
+                make.centerX.equalToSuperview()
+            }
+            titleLabel.snp.makeConstraints { make in
+                make.centerX.equalToSuperview()
+                make.top.equalToSuperview().inset(35)
+            }
+        }
+    }
+}
diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/search/HistorySearchCell.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/search/HistorySearchCell.swift
new file mode 100644 (file)
index 0000000..f29217f
--- /dev/null
@@ -0,0 +1,169 @@
+//
+//  HistorySearchCell.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/4.
+//
+
+class HistorySearchCell: BaseTableViewCell {
+    let selectedIcon = UIImageView()
+    let bgView = UIView()
+    let playIcon = UIImageView()
+    let titleLabel = UILabel()
+    let collectionIcon = UIImageView()
+    let durationLabel = UILabel()
+    let dateLabel = UILabel()
+    let typeLabel = UILabel()
+    var selectedCallback:((_ cell:HistorySearchCell)->Void)?
+    
+//    var model:RecordHistoryModel?{
+//        didSet{
+//            guard let model = model else { return  }
+//            titleLabel.text = model.name
+//            dateLabel.text = model.createTime
+//            typeLabel.text = model.type.title
+//            durationLabel.text = YLToos.formatSeconds(model.duration)
+//            titleLabel.textColor = model.favorite == "0" ? UIColor.hexString("#121212"):.color(.theme)
+//            collectionIcon.image = UIImage(named: model.favorite == "0" ? "collection_normal":"collection_selected")
+//        }
+//    }
+    var localModel:RecordHistorySelectedModel?{
+        didSet{
+            guard let localModel = localModel else { return  }
+            titleLabel.text = localModel.model.name
+            dateLabel.text = localModel.model.createTime
+            typeLabel.text = localModel.model.type.title
+            durationLabel.text = YLToos.formatSeconds(localModel.model.duration)
+            titleLabel.textColor = localModel.model.favorite == "0" ? UIColor.hexString("#121212"):.color(.theme)
+            collectionIcon.image = UIImage(named: localModel.model.favorite == "0" ? "collection_normal":"collection_selected")
+            selectedIcon.image = UIImage(named: localModel.isSelcted ? "btn_selected":"btn_normal")
+        }
+    }
+    
+    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    override func setupViews() {
+        contentView.addSubviews([selectedIcon,bgView])
+        bgView.addSubviews([playIcon,titleLabel,durationLabel,collectionIcon,typeLabel,dateLabel])
+        
+        backgroundColor = .rgb(0xF7F7F7)
+        contentView.backgroundColor = .clear
+        selectionStyle = .none
+        bgView.do { v in
+            v.backgroundColor = UIColor.white
+            v.layer.cornerRadius = 12
+            v.layer.masksToBounds = true
+        }
+        titleLabel.do { (v) in
+            v.textColor = .rgb(0x121212)
+            v.font = .bold(16)
+            v.textAlignment = .left
+            v.text = "周一会议总结"
+        }
+        durationLabel.do { (v) in
+            v.textColor = .rgb(0x666666)
+            v.font = .regular(12)
+            v.textAlignment = .left
+            v.text = "00:32:27"
+        }
+        typeLabel.do { (v) in
+            v.textColor = .rgb(0x666666)
+            v.font = .regular(12)
+            v.textAlignment = .left
+            v.text = "现场录音"
+        }
+        dateLabel.do { (v) in
+            v.textColor = .rgb(0x666666)
+            v.font = .regular(12)
+            v.textAlignment = .left
+            v.text = "2025-06-02 10:28"
+        }
+        collectionIcon.do { v in
+            v.yc.addTap(target: self, action: #selector(collectionAction))
+        }
+        playIcon.do { v in
+            v.image = UIImage(named: "record_play 2")
+        }
+        selectedIcon.do { v in
+            v.image = UIImage(named: "btn_normal")
+            v.contentMode = .center
+            v.yc.addTap(target: self, action: #selector(selectedAction))
+        }
+    }
+    
+    override func setupLayout() {
+        selectedIcon.snp.makeConstraints { (m) in
+            m.size.equalTo(54)
+            m.left.equalToSuperview().inset(0)
+            m.centerY.equalToSuperview()
+        }
+        bgView.snp.makeConstraints { (m) in
+            m.left.equalToSuperview().inset(54)
+            m.right.equalToSuperview().inset(15)
+            m.top.equalToSuperview().inset(5)
+            m.bottom.equalToSuperview().inset(5)
+            m.height.equalTo(74)
+        }
+        
+        playIcon.snp.makeConstraints { (m) in
+            m.width.size.equalTo(30)
+            m.left.equalToSuperview().inset(14)
+            m.centerY.equalToSuperview()
+        }
+        titleLabel.snp.makeConstraints { (m) in
+            m.top.equalTo(16)
+            m.left.equalTo(playIcon.snp.right).offset(6)
+            m.right.lessThanOrEqualTo(typeLabel.snp.left).offset(-50)
+        }
+        collectionIcon.snp.makeConstraints { (m) in
+            m.size.equalTo(16)
+            m.left.equalTo(titleLabel.snp.right).offset(8)
+            m.centerY.equalTo(titleLabel)
+        }
+        
+        durationLabel.snp.makeConstraints { (m) in
+            m.bottom.equalToSuperview().inset(16)
+            m.left.equalTo(titleLabel)
+        }
+        typeLabel.snp.makeConstraints { (m) in
+            m.top.equalToSuperview().inset(16)
+            m.right.equalToSuperview().inset(14)
+        }
+        dateLabel.snp.makeConstraints { (m) in
+            m.bottom.equalToSuperview().inset(16)
+            m.right.equalTo(typeLabel)
+        }
+    }
+    @objc func collectionAction(){
+//        if let model = model {
+//            model.favorite = model.favorite == "0" ? "1":"0"
+//            collectionIcon.image = UIImage(named: model.favorite == "0" ? "collection_normal":"collection_selected")
+//            
+//            var params:[String:Any] = [:]
+//            params["recordId"] = model.id
+//            params["favorite"] = model.favorite
+//            let api = CommomApi.voiceRecordCollection(param: params)
+//            HttpClient.request(api) { (model:RecordHistoryModelRoot,res) in
+//            }
+//        }
+//        else
+//        if let localModel = localModel{
+//            localModel.model.favorite = localModel.model.favorite == "0" ? "1":"0"
+//            collectionIcon.image = UIImage(named: localModel.model.favorite == "0" ? "collection_normal":"collection_selected")
+//            RecordCache.shared.add(localModel.model)
+//        }
+    }
+    @objc func selectedAction(){
+        if let mode = localModel{
+            mode.isSelcted = !mode.isSelcted
+            self.localModel = mode
+        }
+        selectedCallback?(self)
+    }
+
+}
diff --git a/ShenYin/ShenYin/UI/Home/HistoryRecord/search/HistorySelectedVC.swift b/ShenYin/ShenYin/UI/Home/HistoryRecord/search/HistorySelectedVC.swift
new file mode 100644 (file)
index 0000000..9db7143
--- /dev/null
@@ -0,0 +1,195 @@
+//
+//  Untitled.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/4.
+//
+
+import UIKit
+class HistorySelectedVC: BaseVC{
+    let tableView = UITableView(frame: .zero, style: .plain)
+    let bottomView = HistorySearchBottomView()
+    var list = [RecordHistorySelectedModel]()
+    var deleteCallback:((_ list:[RecordDetailModel])->Void)?
+    
+    init(data:[RecordDetailModel]) {
+        var tempList = [RecordHistorySelectedModel]()
+        for item in data {
+            let model = RecordHistorySelectedModel()
+            model.model = item
+            tempList.append(model)
+        }
+        list = tempList
+        tableView.reloadData()
+        super.init()
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        // Do any additional setup after loading the view.
+    }
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        tableView.reloadData()
+    }
+    override func setupViews() {
+        navBar.do { v in
+            v.titleLabel.font = .bold(16)
+            v.title = "已选择0项"
+//            v.leftView.backgroundColor = .red
+            v.leftView.setImage(nil, for: .normal)
+            v.rightView.isHidden = false
+//            v.rightView.backgroundColor = .red
+            
+            v.leftView.snp.remakeConstraints { (m) in
+                m.leading.equalToSuperview()
+                m.top.equalTo(kNavTop)
+                m.bottom.equalToSuperview()
+                m.width.greaterThanOrEqualTo(58)
+            }
+            v.rightView.snp.remakeConstraints { (m) in
+                m.right.equalToSuperview()
+                m.top.equalTo(kNavTop)
+                m.bottom.equalToSuperview()
+                m.width.greaterThanOrEqualTo(58)
+            }
+            
+            let leftLabel = UILabel()
+            leftLabel.text = "全选"
+            leftLabel.textColor = .hexString("#666666")
+            leftLabel.yc.setBorder(width: 1, color: .hexString("#666666"), radius: 11)
+            leftLabel.font = .regular(12)
+            leftLabel.textAlignment = .center
+            leftLabel.yc.addTap(target: self, action: #selector(allSelctedAction))
+            v.leftView.addSubview(leftLabel)
+            leftLabel.snp.remakeConstraints { (m) in
+                m.leading.equalToSuperview().inset(18)
+                m.height.equalTo(22)
+                m.centerY.equalToSuperview()
+                m.width.greaterThanOrEqualTo(40)
+            }
+            let rightLabel = UILabel()
+            rightLabel.textAlignment = .center
+            rightLabel.text = "完成"
+            rightLabel.textColor = .white
+            rightLabel.backgroundColor = .color(.theme)
+            rightLabel.font = .regular(12)
+            rightLabel.yc.setCornerRadius(11)
+            rightLabel.yc.addTap(target: self, action: #selector(doneAction))
+
+            v.rightView.addSubview(rightLabel)
+            rightLabel.snp.remakeConstraints { (m) in
+                m.right.equalToSuperview().inset(18)
+                m.height.equalTo(22)
+                m.centerY.equalTo(v.titleLabel)
+                m.width.greaterThanOrEqualTo(40)
+            }
+        }
+        
+        tableView.do { v in
+            v.backgroundColor = .rgb(0xF7F7F7)
+            v.delegate = self
+            v.dataSource = self
+            v.estimatedRowHeight = 84
+            v.separatorStyle = .none
+            //            v.isEditing = true
+            v.register(HistorySearchCell.self, forCellReuseIdentifier: HistorySearchCell.reuseID)
+        }
+        bottomView.do { v in
+            v.outItem.yc.addTap(target: self, action: #selector(outputAction))
+            v.deleteItem.yc.addTap(target: self, action: #selector(deleteAction))
+        }
+        view.addSubviews([bottomView,tableView])
+    }
+    override func setupLayout() {
+        bottomView.snp.makeConstraints { make in
+            make.left.right.bottom.equalToSuperview()
+            make.height.equalTo(95)
+        }
+        tableView.snp.makeConstraints { make in
+            make.left.right.equalToSuperview()
+            make.top.equalTo(kNavBtm)
+            make.bottom.equalToSuperview().inset(95)
+        }
+    }
+
+    
+    @objc func allSelctedAction(){
+        for item in list {
+            item.isSelcted = true
+        }
+        tableView.reloadData()
+        selectedAction()
+    }
+    @objc func doneAction(){
+        App.shared.popPage(animated: false)
+    }
+    func selectedAction(){
+        let selectedList = list.filter({$0.isSelcted})
+        navBar.title = "已选择\(selectedList.count)项"
+    }
+    @objc func outputAction(){
+        var tempList = [RecordDetailModel]()
+        for item in list {
+            if item.isSelcted {
+                tempList.append(item.model)
+            }
+        }
+        App.shared.goToPage(RecordShareVC(localList: tempList))
+    }
+    @objc func deleteAction(){
+        var tempList = [RecordDetailModel]()
+        for item in list {
+            if item.isSelcted {
+                tempList.append(item.model)
+            }
+        }
+        if tempList.count == 0 {
+            return
+        }
+        
+        CustomAlert.showWith(title: "您要删除这些录音吗?", messgage: "删除后,这些记录将无法恢复。", cancelCallback: {}) {[weak self] in
+            guard let self = self else{return}
+            for item in tempList{
+                list.removeAll(where: {$0.model.createTime == item.createTime})
+            }
+            RecordCache.shared.remove(list:tempList)
+            deleteCallback?(tempList)
+            tableView.reloadData()
+            
+        }
+    }
+}
+
+extension HistorySelectedVC:UITableViewDelegate,UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return list.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = HistorySearchCell.cell(withTableView: tableView, indexPath: indexPath)
+        cell.localModel = list[indexPath.row]
+        cell.selectedCallback = {[weak self] cell in
+            self?.selectedAction()
+        }
+        return cell
+    }
+//    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+//        let data = list[indexPath.row]
+//
+//    }
+//    // 实现滑动删除功能
+//    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
+//        if editingStyle == .delete {
+//            // 从数据源中移除对应数据
+//            list.remove(at: indexPath.row)
+//            // 从表格中删除对应行
+//            tableView.deleteRows(at: [indexPath], with:.fade)
+//        }
+//    }
+//    
+//    // 允许编辑
+//    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
+//        return true
+//    }
+}
index ade5b3d..837bccb 100644 (file)
@@ -325,19 +325,24 @@ extension RecordDetailVC:RecordDetailBarDelegate{
             guard let data = data else {
                 return
             }
-            App.shared.goToPage(HistoryRecordShareVC(record: data,fileId: remoteData?.fileId))
+            if let cloud = remoteData {
+                App.shared.goToPage(RecordShareVC(cloudList: [cloud]))
+            }
+            else{
+                App.shared.goToPage(RecordShareVC(localList: [data]))
+            }
         }
         v.fontCallback = {[weak self] in
             guard let self = self  else {
                 return
             }
-            App.shared.goToPage(HistoryFontVC())
+            App.shared.goToPage(RecordFontVC())
         }
         v.deleteCallback = {[weak self] in
             guard let self = self  else {
                 return
             }
-            CustomAlert.showWith(title: "æ\8f\90示", messgage: "ç¡®å®\9aè¦\81å\88 é\99¤é\80\89中ç\9a\84项ç\9b®å\90\97ï¼\9f", cancelCallback: {}) {
+            CustomAlert.showWith(title: "æ\82¨è¦\81å\88 é\99¤è¿\99æ\9d¡å½\95é\9f³å\90\97?", messgage: "å\88 é\99¤å\90\8eï¼\8c此记å½\95å°\86æ\97 æ³\95æ\81¢å¤\8d", cancelCallback: {}) {
                 if let data = self.remoteData{
                     let api = CommomApi.deleteVoiceRecord(recordId: data.id)
                     HttpClient.request(api) {(model:ResponseBaseModel,res) in
@@ -413,6 +418,9 @@ extension RecordDetailVC{
         params["duration"] = YLToos.getDuration(data!.path)
         params["fileId"] = fileId
         params["summary"] = data!.origin
+        params["sourceLanguage"] = data!.from.value
+        params["targetLanguage"] = data!.to.value
+        
         Logger.debug(params)
         HttpClient.request(.voiceRecord(param: params)) {[weak self](model:AppUpdateModelRoot,res) in
             if model.isSuccess{
index 35dbb98..1494743 100644 (file)
@@ -21,6 +21,7 @@ class RecordTranslateVC:BaseVC{
         view.addSubviews([toolBar,textView,translateTextView])
         
         navBar.title = "同传听译"
+        navBar.rightView.isHidden = false
         navBar.rightView.setImage(UIImage(named: "help_icon"), for: .normal)
         
         toolBar.do { v in
@@ -137,7 +138,7 @@ extension RecordTranslateVC:RecordBarDelegate{
             JoyGemSIManager.shared().stop()
             bar.startAnimating {[weak self] in
                 guard let self = self else{return}
-                guard let path = recordFilePath else{return}
+                guard let path = recordFilePath,let wavPath = DCFolder.recordFileResave(srcPath: path) else{return}
                 let v = PopTextField()
                 v.show()
                 v.cancelTap = {
@@ -149,9 +150,9 @@ extension RecordTranslateVC:RecordBarDelegate{
                     let model = RecordDetailModel()
                     model.type = .translate
                     model.from = bar.leftItem.language
-                    model.to = .english
+                    model.to = bar.rightItem.language
                     model.name = text
-                    model.path = path
+                    model.path = wavPath
                     model.duration = YLToos.getDuration(model.path)
                     model.origin = textView.text ?? ""
                     model.translate = translateTextView.text ?? ""
index 54d00ed..6e53290 100644 (file)
@@ -17,7 +17,8 @@ class AddDeviceVC:BaseVC{
     
     override func setupViews() {
         self.navBar.title = ""
-        navBar.rightView.setImage(UIImage(named: "tips_cion"), for: .normal)
+        
+//        navBar.rightView.setImage(UIImage(named: "tips_cion"), for: .normal)
         view.addSubviews([titleLabel,addView,scanView])
         titleLabel.do { v in
             v.text = "从这里选择一项用来添加设备"
index d9676eb..5c867b3 100644 (file)
@@ -26,6 +26,7 @@ class BleScanVC: BaseVC{
         // Do any additional setup after loading the view.
     }
     override func setupViews() {
+        navBar.rightView.isHidden = false
         navBar.rightView.setImage(UIImage(named: "refresh_icon"), for: .normal)
         tableView.do { v in
             v.backgroundColor = .rgb(0xF7F7F7)
@@ -72,6 +73,7 @@ class BleScanVC: BaseVC{
             }
             else{
                 self.scanState = .none
+//                self.headerView.rippleView.stopAnimating()
             }
             self.tableView.reloadData()
         }
@@ -178,6 +180,7 @@ class ScanStateCell: BaseTableViewCell {
     }
     override func setupViews() {
 //        backgroundColor = .green
+        backgroundColor = UIColor.color(.bgColor)
         addSubviews([titleLabel,desLabel])
         titleLabel.do { (v) in
             v.textColor = .rgb(0x121212)
@@ -233,7 +236,7 @@ class BleScanCell: BaseTableViewCell {
         contentView.backgroundColor = .clear
         selectionStyle = .none
         bgView.do { v in
-            v.backgroundColor = UIColor.white
+//            v.backgroundColor = UIColor.white
             v.layer.cornerRadius = 8
             v.layer.masksToBounds = true
         }
index e61d552..330d40f 100644 (file)
@@ -62,10 +62,6 @@ class TranslateCell:BaseTableViewCell{
         }
         contentView.addSubview(bgView)
         bgView.addSubviews([originalLabel,translationLabel,line,copyIcon])
-        
-        
-        //        let brokenLineView = BrokenLineView(frame: CGRect(x: 0, y: 0, width: 300, height:100))
-        //        addSubview(brokenLineView)
     }
     
     override func setupLayout() {
index f0af319..24dcdc2 100644 (file)
@@ -18,6 +18,9 @@ class App{
     }
     
     func didLogin(){
+        // 加载目录
+        Logger.debug(DCFolder.user)
+        Logger.debug(DCFolder.record)
         window?.rootViewController = BaseTC()
     }
     func goToLogin(){
index 0a99f64..de56146 100644 (file)
@@ -18,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
         
         App.shared.window = window
         window?.rootViewController = BaseTC()
-//        window?.rootViewController = UINavigationController(rootViewController: FeedbackCommitVC())
+//        window?.rootViewController = UINavigationController(rootViewController: HistorySelectedVC())
         AppLaunch.start()
     }
 
index 3f1de92..dfffb97 100644 (file)
@@ -39,7 +39,7 @@ class AccountSetVC: BaseVC{
     }
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
-        CacheManager.shared.tokenIsValid(){[weak self] in
+        CacheManager.shared.user.tokenIsValid(){[weak self] in
             self?.header.updateUI()
         }
     }
index 92e5920..9bf9893 100644 (file)
@@ -24,6 +24,7 @@ class FeedbackCommitVC:BaseVC{
     }
     override func setupViews() {
         self.navBar.title = "帮助与反馈"
+        navBar.rightView.isHidden = false
         navBar.rightView.setImage(UIImage(named: "feedback_history"), for: .normal)
 
         view.addSubview(bgView)
index 8e9defa..0c1def9 100644 (file)
@@ -7,8 +7,10 @@
 
 import UIKit
 class InstructionsVC: BaseVC{
+    let bar = SearchBar()
     let tableView = UITableView(frame: .zero, style: .plain)
-    var list = [InstructionsModel]()
+    var orginList = [InstructionsModel]()
+    var searchList = [InstructionsModel]()
     
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -16,6 +18,11 @@ class InstructionsVC: BaseVC{
     }
     override func setupViews() {
         self.navBar.title = "电子说明书"
+        
+        view.addSubviews([tableView,bar])
+        bar.do { v in
+            v.delegate = self
+        }
         tableView.do { v in
             v.backgroundColor = .color(.bgColor)
             v.delegate = self
@@ -24,15 +31,20 @@ class InstructionsVC: BaseVC{
             v.sectionHeaderHeight = 52
             v.separatorStyle = .none
             v.register(CustomCell1.self, forCellReuseIdentifier: CustomCell1.reuseID)
-            v.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: kScreenHei, height: 20))
+//            v.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: kScreenHei, height: 20))
         }
                 
-        view.addSubview(tableView)
+        
     }
     override func setupLayout() {
+        bar.snp.makeConstraints { make in
+            make.top.equalToSuperview().inset(kNavBtm + 10)
+            make.left.right.equalToSuperview()
+            make.height.equalTo(44)
+        }
         tableView.snp.makeConstraints { make in
             make.left.right.bottom.equalToSuperview()
-            make.top.equalTo(kNavBtm)
+            make.top.equalTo(bar.snp.bottom).offset(10)
         }
     }
     override func setupData() {
@@ -40,7 +52,8 @@ class InstructionsVC: BaseVC{
         HttpClient.request(api) {[weak self] (model:InstructionsModelRoot,res) in
             guard let self = self else {return}
             if model.isSuccess{
-                self.list = model.data
+                self.orginList = model.data
+                self.searchList = self.orginList
                 self.tableView.reloadData()
             }
         }
@@ -49,28 +62,48 @@ class InstructionsVC: BaseVC{
 
 extension InstructionsVC:UITableViewDelegate,UITableViewDataSource {
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return list.count
+        return searchList.count
     }
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = CustomCell1.cell(withTableView: tableView, indexPath: indexPath)
-        cell.titleLabel.text = list[indexPath.row].name
+        cell.titleLabel.text = searchList[indexPath.row].name
         let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
         if indexPath.row == 0 {
             cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 12, height: 12))
         }
-        else if indexPath.row == list.count - 1{
+        else if indexPath.row == searchList.count - 1{
             cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 12, height: 12))
         }
         else{
             cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0))
         }
-        if list.count == 1 {
+        if searchList.count == 1 {
             cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 12, height: 12))
         }
         return cell
     }
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        let data = list[indexPath.row]
+        let data = searchList[indexPath.row]
         App.shared.goToPage(InstructionsDetailVC(productId: data.id))
     }
 }
+
+extension InstructionsVC:SearchBarDelegate{
+    func searchBarTextDidChange(_ toolBar: SearchBar, _ text: String) {
+        if text.isEmpty {
+            searchList = orginList
+        }
+        else{
+            var list =  [InstructionsModel]()
+            for item in orginList {
+                if item.name.contains(text) {
+                    list.append(item)
+                }
+            }
+            searchList = list
+        }
+        tableView.reloadData()
+    }
+    
+    
+}
diff --git a/ShenYin/ShenYin/UI/Mine/Instructions/SearchBar.swift b/ShenYin/ShenYin/UI/Mine/Instructions/SearchBar.swift
new file mode 100644 (file)
index 0000000..4af4687
--- /dev/null
@@ -0,0 +1,74 @@
+//
+//  Untitled.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/4.
+//
+
+
+@objc protocol SearchBarDelegate {
+    func searchBarTextDidChange(_ toolBar:SearchBar,_ text:String)
+}
+
+class SearchBar:UIView{
+    let bgView = UIView()
+    let textField = UITextField()
+    let icon = UIImageView(image: UIImage(named: "search_2"), )
+    
+    weak var delegate:SearchBarDelegate?
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        backgroundColor = .clear
+        
+        bgView.do { v in
+            v.yc.setCornerRadius(8)
+            v.backgroundColor = .white
+        }
+
+        
+        textField.do { v in
+            v.clearButtonMode = .whileEditing
+            v.backgroundColor = .clear
+            v.placeholder = "输入型号查询"
+            v.returnKeyType = .done
+            v.delegate = self
+            v.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
+        }
+        
+        addSubview(bgView)
+        bgView.addSubview(icon)
+        bgView.addSubview(textField)
+        
+        bgView.snp.makeConstraints { make in
+            make.left.right.equalToSuperview().inset(18)
+            make.top.bottom.equalToSuperview()
+        }
+        icon.snp.makeConstraints { make in
+            make.size.equalTo(18)
+            make.centerY.equalToSuperview()
+            make.left.equalToSuperview().inset(12)
+        }
+        textField.snp.makeConstraints { make in
+            make.left.equalToSuperview().inset(34)
+            make.top.bottom.equalToSuperview()
+            make.right.equalToSuperview()
+        }
+    }
+    
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    @objc func textFieldDidChange(_ textField:UITextField){
+        delegate?.searchBarTextDidChange(self, textField.text ?? "")
+    }
+    
+}
+extension SearchBar:UITextFieldDelegate{
+    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+        textField.resignFirstResponder()
+        return false
+    }
+}
index 3193418..9600c70 100644 (file)
@@ -39,7 +39,7 @@ class LoginVM:BaseVM{
         let api = CommomApi.login(param: param)
         HttpClient.request(api) { (model:LoginModel,res) in
             if model.isSuccess{
-                CacheManager.shared.login(token: model.data)
+                CacheManager.shared.user.login(token: model.data)
             }
             completion(model)
         }
@@ -48,7 +48,7 @@ class LoginVM:BaseVM{
         let api = CommomApi.phoneCodeLogin(param: param)
         HttpClient.request(api) { (model:LoginModel,res) in
             if model.isSuccess{
-                CacheManager.shared.login(token: model.data)
+                CacheManager.shared.user.login(token: model.data)
             }
             completion(model)
         }
@@ -57,7 +57,7 @@ class LoginVM:BaseVM{
         let api = CommomApi.emailCodeLogin(param: param)
         HttpClient.request(api) { (model:LoginModel,res) in
             if model.isSuccess{
-                CacheManager.shared.login(token: model.data)
+                CacheManager.shared.user.login(token: model.data)
             }
             completion(model)
         }
@@ -66,7 +66,7 @@ class LoginVM:BaseVM{
         let api = CommomApi.appleLogin(param: param)
         HttpClient.request(api) { (model:LoginModel,res) in
             if model.isSuccess{
-                CacheManager.shared.login(token: model.data)
+                CacheManager.shared.user.login(token: model.data)
             }
             completion(model)
         }
@@ -75,7 +75,7 @@ class LoginVM:BaseVM{
         let api = CommomApi.wechatLogin(param: param)
         HttpClient.request(api) { (model:LoginModel,res) in
             if model.isSuccess{
-                CacheManager.shared.login(token: model.data)
+                CacheManager.shared.user.login(token: model.data)
             }
             completion(model)
         }
index b8d0ee6..ffd799c 100644 (file)
@@ -65,12 +65,12 @@ class MoreSetVC: BaseVC{
     }
     @objc func swictAction(){
         CustomAlert.showWith(title: "提示", messgage: "确定要切换账号吗?", cancelCallback: {}) {
-            CacheManager.shared.logout()
+            CacheManager.shared.user.logout()
         }
     }
     @objc func logoutAction(){
         CustomAlert.showWith(title: "提示", messgage: "确定要退出登录吗?", cancelCallback: {}) {
-            CacheManager.shared.logout()
+            CacheManager.shared.user.logout()
         }
     }
     @objc func deactivateAction(){
@@ -78,7 +78,7 @@ class MoreSetVC: BaseVC{
             HttpClient.request(.deactivate) { (result:ResponseBaseModel,res) in
                 if result.isSuccess{
                     YLprogressHUD.showSuccess {
-                        CacheManager.shared.logout()
+                        CacheManager.shared.user.logout()
                     }
                 }
                 else{
@@ -118,6 +118,9 @@ extension MoreSetVC:UITableViewDelegate,UITableViewDataSource {
         else{
             cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0))
         }
+        if list.count == 1 {
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 12, height: 12))
+        }
         return cell
     }
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
index 776386f..1d40f09 100644 (file)
@@ -26,7 +26,13 @@ class RevokeVC:BaseVC{
             v.font = .regular(14)
             v.textColor = .hexString("#666666")
             v.numberOfLines = 0
-            v.text = "请您确保您是有权进行本撤回同意隐私政策操作的相关当事人。撤回同意隐私政策,您将放弃以下资产或者权益:\n\n1.数据清空\n将会停止收集您的个人信息。您使用DeepChords应用过程中产生的数据将被清空;手机中DeepChords应用的缓存数据将被清除。\n\n2.设备解除绑定\nDeepChords账号中,所有设备会与您当前登录的账号解除绑定,并清除设备之前存储在服务器的数据。存储在物理设备中的数据请通过人工本地物理重置来清除。如果您需要重新使用设备,需要您再次进行绑定并且重新授权。\n\n3.其他\n此举还将撤回您对用户协议的同意,并退出DeepChor ds应用。\n\n请您知悉,撤回同意隐私政策不代表撤回同意前的相关行为和责任得到豁免或减轻。若您需要重新在该账号管理设备,需要您重新授权并绑定。请谨慎操作!"
+            let text = "请您确保您是有权进行本撤回同意隐私政策操作的相关当事人。撤回同意隐私政策,您将放弃以下资产或者权益:\n\n1.数据清空\n将会停止收集您的个人信息。您使用DeepChords应用过程中产生的数据将被清空;手机中DeepChords应用的缓存数据将被清除。\n\n2.设备解除绑定\nDeepChords账号中,所有设备会与您当前登录的账号解除绑定,并清除设备之前存储在服务器的数据。存储在物理设备中的数据请通过人工本地物理重置来清除。如果您需要重新使用设备,需要您再次进行绑定并且重新授权。\n\n3.其他\n此举还将撤回您对用户协议的同意,并退出DeepChor ds应用。\n\n请您知悉,撤回同意隐私政策不代表撤回同意前的相关行为和责任得到豁免或减轻。若您需要重新在该账号管理设备,需要您重新授权并绑定。请谨慎操作!"
+            
+            let attributedString = NSMutableAttributedString(string: text)
+            let paragraphStyle = NSMutableParagraphStyle()
+            paragraphStyle.lineSpacing = 4
+            attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
+            v.attributedText = attributedString
         }
         btn.set(title: "确认撤回", style: .theme4,target: self, action: #selector(btnAction))
     }
index c827b80..3071b3a 100644 (file)
@@ -5,17 +5,3 @@
 //  Created by liangshi on 2025/10/21.
 //
 
-import HandyJSON
-
-class BannerModelRoot:ResponseBaseModel {
-    var data = [BannerModel]()
-}
-
-class BannerModel:HandyJSON {
-    var id = ""
-    var title = ""
-    var cover = ""
-    var url = ""
-    var clickCount = 0
-    required public init() { }
-}
index e464844..b2b3e13 100644 (file)
@@ -56,6 +56,7 @@ final class MineBannerView: BaseView {
             v.setFillColor(UIColor.white.withAlphaComponent(0.4), for: .normal)
             v.setFillColor(.white, for: .selected)
         }
+        config(list: BannerCache.shared.get())
     }
 }
 
index 7315de1..94c8143 100644 (file)
@@ -95,7 +95,7 @@ class MineUserInfoView: BaseView {
         }
     }
     func updateUI(){
-        CacheManager.shared.tokenIsValid(){[weak self] in
+        CacheManager.shared.user.tokenIsValid(){[weak self] in
             guard let self = self else {return}
             titleLabel.text = CacheManager.shared.user.info.userName
             icon.kf.setImage(with:CacheManager.shared.user.info.avatar.yc.asUrl )
@@ -190,7 +190,7 @@ class CloudStorageView: BaseView {
     }
 
     @objc func tapAction(){
-        App.shared.goToPage(CloudHistoryRecordVC())
+        App.shared.goToPage(CloudRecordVC())
     }
 
 }
index cc64a30..00fd206 100644 (file)
@@ -56,6 +56,7 @@ class MineVC: BaseVC{
             guard let self = self else {return}
             if model.isSuccess{
                 headerView.banner.config(list: model.data)
+                BannerCache.shared.saveList(model.data)
             }
         }
         headerView.userInfo.updateUI()
index f0ebad9..dad9f5c 100644 (file)
@@ -8,6 +8,7 @@
 import UIKit
 
 class UpdateVC:BaseVC{
+    let icon = UIImageView(image: UIImage(named: "update_icon"))
     let versionLabel = UILabel()
     let btn = UIButton(type: .custom)
     override func viewDidLoad() {
@@ -16,7 +17,7 @@ class UpdateVC:BaseVC{
         
     }
     override func setupViews() {
-        view.addSubviews([versionLabel,btn])
+        view.addSubviews([icon,versionLabel,btn])
         self.navBar.title = "固件升级"
         versionLabel.do { v in
             v.text = "当前版本\n"
@@ -27,7 +28,7 @@ class UpdateVC:BaseVC{
         }
         btn.do { v in
             v.setTitle("检查更新", for: .normal)
-            v.titleLabel?.font = .regular(12)
+            v.titleLabel?.font = .bold(14)
             v.setTitleColor(.color(.theme), for: .normal)
             v.addTarget(self, action: #selector(tapAction), for: .touchUpInside)
             v.backgroundColor = .hexString("#EDEDED")
@@ -35,9 +36,15 @@ class UpdateVC:BaseVC{
         }
     }
     override func setupLayout() {
+        icon.snp.makeConstraints { make in
+            make.width.equalTo(272)
+            make.height.equalTo(240)
+            make.centerX.equalToSuperview()
+            make.top.equalToSuperview().inset(kNavBtm + 90)
+        }
         versionLabel.snp.makeConstraints { make in
             make.centerX.equalToSuperview()
-            make.centerY.equalToSuperview()
+            make.top.equalTo(icon.snp.bottom).offset(65)
         }
         btn.snp.makeConstraints { make in
             make.bottom.equalToSuperview().inset(82)
@@ -49,10 +56,16 @@ class UpdateVC:BaseVC{
     override func setupData() {
         DeviceVM.getVersion {[weak self] version in
             guard let self = self else{return}
-            guard let version = version else{
-                return
-            }
-            versionLabel.text = "当前版本\n" + "\(version)"
+            let version = version ?? "--"
+            let allText = "当前版本\n" + "V\(version)"
+            let attributedString = NSMutableAttributedString(string: allText)
+            let paragraphStyle = NSMutableParagraphStyle()
+            paragraphStyle.lineSpacing = 7
+            attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
+            attributedString.addAttribute(.font, value: UIFont.bold(16), range: NSRange(location: 0, length: attributedString.length))
+            attributedString.addAttribute(.font, value: UIFont.bold(14), range: NSRange(location: 4, length: version.count + 1))
+            versionLabel.attributedText = attributedString
+            versionLabel.textAlignment = .center
         }
     }
     @objc func tapAction(){
index 3e9a8b3..f21e3a5 100644 (file)
@@ -158,7 +158,7 @@ extension UserInfoVC{
         }
     }
     func refreshUI(){
-        CacheManager.shared.tokenIsValid(){[weak self] in
+        CacheManager.shared.user.tokenIsValid(){[weak self] in
             self?.tableView.reloadData()
         }
         
index 1338f87..caae25b 100644 (file)
@@ -121,9 +121,9 @@ class ModeEffectsView: BaseView {
                 make.height.equalTo(h)
                 make.left.equalToSuperview().inset(x)
                 make.top.equalToSuperview().inset(y)
-                if index == list.count - 1 {
-                    make.bottom.equalToSuperview().inset(14)
-                }
+//                if index == list.count - 1 {
+//                    make.bottom.equalToSuperview().inset(14)
+//                }
             }
         }
         NotificationManager.add(self, selector: #selector(deviceChange(_:)), forName: .deviceValidChange)
@@ -184,7 +184,7 @@ class ModeEffectsView: BaseView {
             addSubviews([icon,titleLabel,selectedIcon])
             icon.do { v in
                 v.yc.setCornerRadius(8)
-                v.contentMode = .center
+//                v.contentMode = .scaleAspectFill
             }
             titleLabel.do { (v) in
                 v.textColor = .white
index faf8df4..e96c8f2 100644 (file)
@@ -6,22 +6,22 @@
 //
 
 class BrokenLineView: UIView {
-//    override func draw(_ rect: CGRect) {
-//        super.draw(rect)
-//        let path = UIBezierPath()
-//        let lineWidth: CGFloat = 0.5
-//        let dashLength: CGFloat = 5.0
-//        let spaceLength: CGFloat = 3.0
-//        let startPoint = CGPoint(x: 0, y: bounds.midY)
-//        let endPoint = CGPoint(x: bounds.width, y: bounds.midY)
-//        path.move(to: startPoint)
-//        path.addLine(to: endPoint)
-//        let shapeLayer = CAShapeLayer()
-//        shapeLayer.path = path.cgPath
-//        shapeLayer.strokeColor = UIColor.hexString("#93C6EC").cgColor
-//        shapeLayer.fillColor = UIColor.clear.cgColor
-//        shapeLayer.lineWidth = lineWidth
-//        shapeLayer.lineDashPattern = [NSNumber(value: dashLength), NSNumber(value: spaceLength)]
-//        layer.addSublayer(shapeLayer)
-//    }
+    override func draw(_ rect: CGRect) {
+        super.draw(rect)
+        let path = UIBezierPath()
+        let lineWidth: CGFloat = 0.5
+        let dashLength: CGFloat = 5.0
+        let spaceLength: CGFloat = 3.0
+        let startPoint = CGPoint(x: 0, y: bounds.midY)
+        let endPoint = CGPoint(x: bounds.width, y: bounds.midY)
+        path.move(to: startPoint)
+        path.addLine(to: endPoint)
+        let shapeLayer = CAShapeLayer()
+        shapeLayer.path = path.cgPath
+        shapeLayer.strokeColor = UIColor.hexString("#93C6EC").cgColor
+        shapeLayer.fillColor = UIColor.clear.cgColor
+        shapeLayer.lineWidth = lineWidth
+        shapeLayer.lineDashPattern = [NSNumber(value: dashLength), NSNumber(value: spaceLength)]
+        layer.addSublayer(shapeLayer)
+    }
 }
diff --git a/ShenYin/ShenYin/UI/common/Cache/DeviceCache.swift b/ShenYin/ShenYin/UI/common/Cache/DeviceCache.swift
deleted file mode 100644 (file)
index b7560df..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-//  DeviceCache.swift
-//  ShenYin
-//
-//  Created by liangshi on 2025/9/26.
-//
-
-class DeviceModel: NSObject, NSCoding {
-    var name = ""
-    var uuid = ""
-    
-    init(name: String, uuid: String) {
-        self.name = name
-        self.uuid = uuid
-    }
-    
-    func encode(with aCoder: NSCoder) {
-        aCoder.encode(name, forKey: "name")
-        aCoder.encode(uuid, forKey: "uuid")
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        name = aDecoder.decodeObject(forKey: "name") as? String ?? ""
-        uuid = aDecoder.decodeObject(forKey: "uuid") as? String ?? ""
-    }
-}
-
-class DeviceCache {
-    static let shared = DeviceCache()
-    private let userDefaults = UserDefaults.standard
-    private let deviceListKey = "deviceListKey"
-    
-    func saveDevice(_ device: DeviceModel) {
-        var deviceList = getDeviceList()
-        if let index = deviceList.firstIndex(where: { $0.uuid == device.uuid }) {
-            deviceList[index] = device
-        } else {
-            deviceList.append(device)
-        }
-        let encoder = NSKeyedArchiver.archivedData(withRootObject: deviceList)
-        userDefaults.set(encoder, forKey: deviceListKey)
-        userDefaults.synchronize()
-    }
-    
-    func getDeviceList() -> [DeviceModel] {
-        if let data = userDefaults.data(forKey: deviceListKey),
-           let deviceList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [DeviceModel] {
-            return deviceList
-        }
-        return []
-    }
-}
index 68882d2..d9a921b 100644 (file)
@@ -18,6 +18,7 @@ class LogVC: BaseVC {
         navBar.title = "log"
         view.backgroundColor = .white
         navBar.title = "log"
+        navBar.rightView.isHidden = false
         navBar.rightView.setTitle("保存", for: .normal)
         
         textView.isEditable = false
index 1115c80..b2beb38 100644 (file)
@@ -80,7 +80,7 @@ class PopTextField: BasePresentView2 {
 
         }
         textFiled.do { v in
-//            v.text = Date().format(.full3)
+            v.text = Date().format(.full3)
             v.placeholder = "请输入录音名字"
             v.font = .bold(16)
             v.textColor = .hexString("#121212")
diff --git a/ShenYin/ShenYin/UI/common/pop/RecordPopView.swift b/ShenYin/ShenYin/UI/common/pop/RecordPopView.swift
new file mode 100644 (file)
index 0000000..bfd329c
--- /dev/null
@@ -0,0 +1,146 @@
+//
+//  RecordPopView.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/11/3.
+//
+
+import UIKit
+
+class RecordPopView: BasePresentView2 {
+    let contentView = UIView()
+    fileprivate let renameItem = MenuItem()
+    fileprivate let sharedItem = MenuItem()
+    fileprivate let deleteItem = MenuItem()
+    fileprivate let selectedItem = MenuItem()
+    
+    var hidenCallback:(()->Void)?
+    var shareCallback:(()->Void)?
+    var renameCallback:(()->Void)?
+    var deleteCallback:(()->Void)?
+    var selectedCallback:(()->Void)?
+    var contentTop = 0.0{
+        didSet{
+            setupStyle()
+        }
+    }
+    
+    init() {
+        super.init(backgroundEnabled: true)
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func setupSubView() {
+        super.setupSubView()
+        addSubview(contentView)
+        contentView.addSubviews([renameItem,sharedItem,deleteItem,selectedItem])
+        
+        renameItem.do { v in
+            v.label.text = "重命名"
+            v.yc.addTap(target: self, action: #selector(renameAction))
+        }
+        sharedItem.do { v in
+            v.label.text = "分享"
+            v.yc.addTap(target: self, action: #selector(sharedAction))
+        }
+        deleteItem.do { v in
+            v.label.text = "删除"
+            v.yc.addTap(target: self, action: #selector(deleteAction))
+        }
+        selectedItem.do { v in
+            v.label.text = "多选"
+            v.yc.addTap(target: self, action: #selector(selectedAction))
+        }
+    }
+    override func setupLayout() {
+        super.setupLayout()
+        contentView.do { (v) in
+            v.backgroundColor = .white
+            v.yc.setCornerRadius(12)
+        }
+        renameItem.snp.makeConstraints { make in
+            make.left.equalToSuperview()
+            make.top.equalToSuperview().inset(12)
+            make.width.equalToSuperview()
+            make.height.equalTo(34)
+        }
+        sharedItem.snp.makeConstraints { make in
+            make.left.equalToSuperview()
+            make.top.equalTo(renameItem.snp.bottom)
+            make.width.equalToSuperview()
+            make.height.equalTo(34)
+        }
+        deleteItem.snp.makeConstraints { make in
+            make.left.equalToSuperview()
+            make.top.equalTo(sharedItem.snp.bottom)
+            make.width.equalToSuperview()
+            make.height.equalTo(34)
+        }
+        selectedItem.snp.makeConstraints { make in
+            make.left.equalToSuperview()
+            make.top.equalTo(deleteItem.snp.bottom)
+            make.width.equalToSuperview()
+            make.height.equalTo(34)
+        }
+    }
+    
+    override func setupStyle() {
+        super.setupStyle()
+        contentView.snp.remakeConstraints { make in
+            make.right.equalToSuperview().inset(18)
+            make.height.equalTo(166)
+            make.width.equalTo(160)
+            
+            if contentTop > kScreenHei - 200{
+                make.top.equalToSuperview().offset(kScreenHei - 200)
+            }
+            else{
+                make.top.equalToSuperview().offset(contentTop)
+            }
+        }
+        
+    }
+    @objc func renameAction(){
+        renameCallback?()
+        hide()
+    }
+    @objc func sharedAction(){
+        shareCallback?()
+        hide()
+    }
+    @objc func deleteAction(){
+        deleteCallback?()
+        hide()
+    }
+    @objc func selectedAction(){
+        selectedCallback?()
+        hide()
+    }
+    override func hide() {
+        hidenCallback?()
+        super.hide()
+    }
+}
+
+fileprivate class MenuItem: BaseView {
+    let label = UILabel()
+    
+    override func setupViews() {
+        addSubviews([label])
+        label.do { v in
+            v.font = .regular(16)
+            v.textColor = .hexString("#121212")
+            
+        }
+
+    }
+    override func setupLayout() {
+        label.snp.makeConstraints { make in
+            make.left.equalToSuperview().inset(18)
+            make.centerY.equalToSuperview()
+        }
+    }
+}