登录注册接口调试
authorliangzhenying <867174124@qq.com>
Mon, 29 Sep 2025 07:18:22 +0000 (15:18 +0800)
committerliangzhenying <867174124@qq.com>
Mon, 29 Sep 2025 07:18:22 +0000 (15:18 +0800)
31 files changed:
ShenYin/Podfile.lock
ShenYin/ShenYin/Api/HttpClient.swift
ShenYin/ShenYin/Api/HttpClient2.swift [new file with mode: 0644]
ShenYin/ShenYin/Api/ResponseBaseModel.swift
ShenYin/ShenYin/Info.plist
ShenYin/ShenYin/Tools/AESEncryption.swift [new file with mode: 0644]
ShenYin/ShenYin/Tools/Date.swift
ShenYin/ShenYin/UI/Base/WebView/BaseWebViewController.swift
ShenYin/ShenYin/UI/Home/HistoryRecord/SelecteCell.swift
ShenYin/ShenYin/UI/Launch/AppLaunch.swift
ShenYin/ShenYin/UI/Launch/AppManager.swift
ShenYin/ShenYin/UI/Launch/BleManager.swift
ShenYin/ShenYin/UI/Launch/SceneDelegate.swift
ShenYin/ShenYin/UI/Mine/Login/Controller/ContryListVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/Login/Controller/ForgetVC.swift
ShenYin/ShenYin/UI/Mine/Login/Controller/LoginVC.swift
ShenYin/ShenYin/UI/Mine/Login/Controller/RegisterVC.swift
ShenYin/ShenYin/UI/Mine/Login/Controller/SmsLoginVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/Login/View/AgreeView.swift
ShenYin/ShenYin/UI/Mine/Login/View/CodeView.swift
ShenYin/ShenYin/UI/Mine/Login/View/CustomInputView.swift
ShenYin/ShenYin/UI/Mine/Login/View/OthersLoginView.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/Login/View/RegionView.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/Login/ViewModel/CountryModel.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/Login/ViewModel/LoginVM.swift
ShenYin/ShenYin/UI/Mine/Login/ViewModel/UserModel.swift
ShenYin/ShenYin/UI/Mine/MoreSetVC.swift
ShenYin/ShenYin/UI/common/Cache/CacheManager.swift
ShenYin/ShenYin/UI/common/Cache/TipsCache.swift
ShenYin/ShenYin/UI/common/NotificationManager.swift
ShenYin/podfile

index 7cb6391..f64e2ec 100644 (file)
@@ -1,5 +1,6 @@
 PODS:
-  - Alamofire (5.10.2)
+  - CryptoSwift (1.8.4)
+  - DeviceKit (5.4.0)
   - DGActivityIndicatorView (2.1.1)
   - FDFullscreenPopGesture (1.1)
   - FSPagerView (0.8.3)
@@ -48,10 +49,6 @@ PODS:
   - JXSegmentedView (1.4.1)
   - Kingfisher (6.3.1)
   - MJRefresh (3.7.9)
-  - Moya (15.0.0):
-    - Moya/Core (= 15.0.0)
-  - Moya/Core (15.0.0):
-    - Alamofire (~> 5.0)
   - netfox (1.21.0)
   - Realm (20.0.3):
     - Realm/Headers (= 20.0.3)
@@ -64,6 +61,7 @@ PODS:
     - SVProgressHUD/Core (= 2.3.1)
   - SVProgressHUD/Core (2.3.1)
   - SwiftDate (7.0.0)
+  - SwiftHTTP (3.0.1)
   - swiftScan (1.2.1)
   - TabPageViewController (0.2.8)
   - TZImagePickerController (3.8.9):
@@ -75,6 +73,8 @@ PODS:
   - YYText (1.0.7)
 
 DEPENDENCIES:
+  - CryptoSwift
+  - DeviceKit (~> 5.4.0)
   - DGActivityIndicatorView
   - FDFullscreenPopGesture
   - FSPagerView
@@ -84,13 +84,13 @@ DEPENDENCIES:
   - JXSegmentedView
   - Kingfisher (~> 6.0)
   - MJRefresh
-  - Moya
   - netfox
   - RealmSwift
   - SnapKit
   - SocketRocket
   - SVProgressHUD
   - SwiftDate
+  - SwiftHTTP
   - swiftScan
   - TabPageViewController
   - TZImagePickerController
@@ -99,7 +99,8 @@ DEPENDENCIES:
 
 SPEC REPOS:
   https://gitee.com/mirrors/CocoaPods-Specs.git:
-    - Alamofire
+    - CryptoSwift
+    - DeviceKit
     - DGActivityIndicatorView
     - FDFullscreenPopGesture
     - FSPagerView
@@ -116,7 +117,6 @@ SPEC REPOS:
     - JXSegmentedView
     - Kingfisher
     - MJRefresh
-    - Moya
     - netfox
     - Realm
     - RealmSwift
@@ -124,6 +124,7 @@ SPEC REPOS:
     - SocketRocket
     - SVProgressHUD
     - SwiftDate
+    - SwiftHTTP
     - swiftScan
     - TabPageViewController
     - TZImagePickerController
@@ -131,7 +132,8 @@ SPEC REPOS:
     - YYText
 
 SPEC CHECKSUMS:
-  Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
+  CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90
+  DeviceKit: bd34bb1766d926c3cc5a5f6e3e9a6dd10148b805
   DGActivityIndicatorView: ff2c76073d79692f724863c8dd38685866a03cb6
   FDFullscreenPopGesture: a8a620179e3d9c40e8e00256dcee1c1a27c6d0f0
   FSPagerView: 670405b2f18e2a87fa37f20b00de783e562c25a8
@@ -148,7 +150,6 @@ SPEC CHECKSUMS:
   JXSegmentedView: cd73555ce2134d1656db2cb383ba9c2f36fb5078
   Kingfisher: 016c8b653a35add51dd34a3aba36b580041acc74
   MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
-  Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
   netfox: 9d5cc727fe7576c4c7688a2504618a156b7d44b7
   Realm: 853e5089d6042dff807bda000277eadfe2da93d2
   RealmSwift: f33c19577cefcbf681345d721fcbc7b42be4c949
@@ -156,12 +157,13 @@ SPEC CHECKSUMS:
   SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
   SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22
   SwiftDate: bbc26e26fc8c0c33fbee8c140c5e8a68293a148a
+  SwiftHTTP: e94f1aa8cde58637dcc0100f6349de31a3337fde
   swiftScan: 0849e7d7c1c641384767a573ee8cb90b88d1ddd9
   TabPageViewController: 8bd4104909754416b35a98f3b72a5b674ab20dd5
   TZImagePickerController: 456f470b5dea97b37226ec7a694994a8663340b2
   YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
   YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
 
-PODFILE CHECKSUM: 57796390fdd051e53f61c569309d2caa20f804e9
+PODFILE CHECKSUM: 1495066f701d18df80b99c90e3a4f415fed5c214
 
 COCOAPODS: 1.16.2
index 3d88d08..1e4ecc1 100644 (file)
 //  Created by incar on 2024/1/9.
 //
 
-import Foundation
-import Moya
-import HandyJSON
-
-// 相关api
-enum CommomApi
-{
-    case sms(phone:String,type:SmsType)
-    case countryList
-    case list(pageNO: Int = 1, pageSize: Int = 10)
-    case login(phone:String,code:String)
-}
-
-// 实现TargetType协议
-extension CommomApi: TargetType
-{
-    // url
-    var baseURL: URL
-    {
-//        return URL(string: "https://app-api.raisound.com")!
-        return URL(string: "https://app-api.deepchords.com")!
-    }
-
-    /// 请求路径
-    var path: String {
-        switch self {
-        case .sms(let phone,let type):
-            return "/uc/sms"
-        case .countryList:
-            return "/uc/country/list"
-        case .list:
-            return "list"
-        case .login(_,_):
-            return "/uc/phone/reg-login"
-        }
-    }
-    
-    /// 请求方式
-    var method: Moya.Method {
-        switch self {
-        case .sms(_,_):
-            return .get
-        case .login(_,_):
-            return .post
-        default:
-            return .get
-        }
-    }
-    
-    /// 解析格式
-    var sampleData: Data {
-        return "{}".data(using: String.Encoding.utf8)!
-    }
-//https://sz.58.com/esf-ajax/activity/ashen_switch/?city_id=4
-    
-    var task: Task {
-        // 公共参数
-        var params: [String: Any] = ["token": "Gz1qYLXeBW8MZuUfDlr9wsAYuVS1cZFMJY9BbaF842L2gRps747o4w=="]
-        
-        // 收集参数
-        switch self {
-        case let .sms(phone, type):
-            params["phone"] = phone
-            params["type"] = type
-        case let .list(pageNO, pageSize):
-            params["pageNO"] = pageNO
-            params["pageSize"] = pageSize
-        case let .login(phone,code):
-            params["phone"] = phone
-            params["smsCode"] = code
-            break
-        default: break
-        }
-        // 发起请求
-        return .requestParameters(parameters: params, encoding: URLEncoding.default)
-    }
-    
-    /// 公共请求头
-    var headers: [String : String]? {
-        return ["devtype": "iOS", "devid": UIDevice().identifierForVendor?.uuidString ?? "unknow"]
-    }
-
-    
-//https://gz.fang.anjuke.com/xinfang/harmony/adviser/adviser/getMainAdviserBlock?client_source=3&limit=100&target_id=523085&target_type=1&adviser_id=&adviser_type=1&loupan_id=523085&layout_id=&page_source=2
-}
-
-class HttpClient{
-    static let instance = HttpClient()
-    let provider = MoyaProvider<CommomApi>()
-    func request<T: HandyJSON>(_ target: CommomApi,completionHandler:@escaping ((T, String) -> Void)){
-        
-
-        provider.request(target){ (response) in
-            Logger.debug("url = \(target.baseURL.absoluteString + target.path)")
-            Logger.debug("param = \(response)")
-            
-            
-            switch response {
-            case .success(let value):
-                let data = value.data
-//                print("reponse data=>",data.hexEncodedString())
-                var responseStr = "response转字符串失败"
-                if let str = String(data: data, encoding: .utf8) {
-                    responseStr = str
-                }
-                Logger.debug("reponse text=>",responseStr)
-                if let res = T.deserialize(from: responseStr){
-                    completionHandler(res,responseStr)
-                    return
-                }
-                completionHandler(ResponseBaseModel() as! T,"解析请求失败")
-                break
-            case .failure(let error):
-                completionHandler(ResponseBaseModel() as! T,"请求失败")
-                Logger.debug("***************\n网络连接失败!\n\(error)\n********************")
-            }
-        }
-    }
-   
-}
+//import Foundation
+//import Moya
+//import HandyJSON
+//import DeviceKit
+//// 相关api
+//enum CommomApi
+//{
+//    case sms(phone:String,type:SmsType)
+//    case countryList
+//    case list(pageNO: Int = 1, pageSize: Int = 10)
+//    case register(param:[String:Any])
+//    case login(phone:String,code:String)
+//}
+//
+//// 实现TargetType协议
+//extension CommomApi: TargetType
+//{
+//    // url
+//    var baseURL: URL
+//    {
+////        return URL(string: "https://app-api.raisound.com")!
+////        http://118.145.178.188:32000/uc/country/list
+////        return URL(string: "https://app-api.deepchords.com")!
+//        return URL(string: "http://118.145.178.188:32000")!
+//    }
+//
+//    /// 请求路径
+//    var path: String {
+//        switch self {
+//        case .sms(_,_):
+//            return "/uc/sms"
+//        case .countryList:
+//            return "/uc/country/list"
+//        case .list:
+//            return "list"
+//        case .register( let param):
+//            return "/uc/sign-up?smsCode=\(param["smsCode"]!)"
+//        case .login(_,_):
+//            return "/uc/phone/reg-login"
+//        }
+//    }
+//    
+//    /// 请求方式
+//    var method: Moya.Method {
+//        switch self {
+//        case .sms(_,_):
+//            return .get
+//        case .register(_):
+//            return .post
+//        case .login(_,_):
+//            return .post
+//        default:
+//            return .get
+//        }
+//    }
+//    
+//    /// 解析格式
+//    var sampleData: Data {
+//        return "{}".data(using: String.Encoding.utf8)!
+//    }
+////https://sz.58.com/esf-ajax/activity/ashen_switch/?city_id=4
+////    let idfv = UIDevice.current.identifierForVendor?.uuidString
+//    var task: Task {
+//        // 公共参数
+////        var params: [String: Any] = ["token": "Gz1qYLXeBW8MZuUfDlr9wsAYuVS1cZFMJY9BbaF842L2gRps747o4w=="]
+//        var params: [String: Any] = [:]
+//
+//        // 收集参数
+//        switch self {
+//        case let .sms(phone, type):
+//            params["phone"] = phone
+//            params["type"] = type
+//        case let .list(pageNO, pageSize):
+//            params["pageNO"] = pageNO
+//            params["pageSize"] = pageSize
+//        case let .register(para):
+//            params += para
+//        case let .login(phone,code):
+//            params["phone"] = phone
+//            params["smsCode"] = code
+//            break
+//        default:
+//            break
+//        }
+//        return .requestParameters(parameters: params, encoding: URLEncoding.default)
+//    }
+//    
+//    /// 公共请求头
+//    var headers: [String : String]? {
+//        return [
+//            "devtype": "iOS",
+//            "devid": UIDevice().identifierForVendor?.uuidString ?? "unknow",
+////            "Content - Type": "application/json"
+//        ]
+//    }
+//
+//    
+////https://gz.fang.anjuke.com/xinfang/harmony/adviser/adviser/getMainAdviserBlock?client_source=3&limit=100&target_id=523085&target_type=1&adviser_id=&adviser_type=1&loupan_id=523085&layout_id=&page_source=2
+//}
+//
+//class HttpClient{
+//    static let instance = HttpClient()
+//    let provider = MoyaProvider<CommomApi>()
+//    
+//    func request<T: HandyJSON>(_ target: CommomApi,completionHandler:@escaping ((T, String) -> Void)){
+//        provider.request(target){ (response) in
+//            Logger.debug("url = \(target.baseURL.absoluteString + target.path)")
+//            Logger.debug("param = \(target.task)")
+//            
+//            
+//            switch response {
+//            case .success(let value):
+//                let data = value.data
+////                print("reponse data=>",data.hexEncodedString())
+//                var responseStr = "response转字符串失败"
+//                if let str = String(data: data, encoding: .utf8) {
+//                    responseStr = str
+//                }
+//                Logger.debug("reponse text=>",responseStr)
+//                if let res = T.deserialize(from: responseStr){
+//                    completionHandler(res,responseStr)
+//                    return
+//                }
+//                completionHandler(T(),"解析请求失败")
+//                break
+//            case .failure(let error):
+//                completionHandler(T(),"请求失败")
+//                Logger.debug("***************\n网络连接失败!\n\(error)\n********************")
+//            }
+//        }
+//    }
+//   
+//}
diff --git a/ShenYin/ShenYin/Api/HttpClient2.swift b/ShenYin/ShenYin/Api/HttpClient2.swift
new file mode 100644 (file)
index 0000000..02d1827
--- /dev/null
@@ -0,0 +1,213 @@
+//
+//  HttpClient2.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/9/28.
+//
+
+import Foundation
+import SwiftHTTP
+import HandyJSON
+import DeviceKit
+
+
+enum RequestMethod{
+    case get
+    case post
+    case put
+}
+
+// 相关api
+enum CommomApi
+{
+    case sms(phone:String,type:SmsType)
+    case countryList
+    case list(pageNO: Int = 1, pageSize: Int = 10)
+    case register(param:[String:Any])
+    case smsLogin(param:[String:Any])
+    case login(param:[String:Any])
+    case deactivate
+    case updatePwd(param:[String:Any])
+    case token
+}
+
+// 实现TargetType协议
+extension CommomApi{
+    // url
+    var baseURL: String
+    {
+        //        return URL(string: "https://app-api.raisound.com")!
+        //        http://118.145.178.188:32000/uc/country/list
+        //        return URL(string: "https://app-api.deepchords.com")!
+        return "http://118.145.178.188:32000"
+    }
+    
+    var url:String{
+        return baseURL + path
+    }
+    
+    /// 请求路径
+    var path: String {
+        switch self {
+        case .sms(_,_):
+            return "/uc/sms"
+        case .countryList:
+            return "/uc/country/list"
+        case .list:
+            return "list"
+        case .register( let param):
+            return "/uc/sign-up?smsCode=\(param["smsCode"]!)"
+        case .smsLogin(_):
+            return "/uc/phone/sms-login"
+        case .login(_):
+            return "/uc/sign-in"
+        case .deactivate:
+            return "/uc/sign-away"
+        case .updatePwd(_):
+            return "/uc/reset"
+        case .token:
+            return "/uc/token"
+        }
+    }
+    
+    var method:RequestMethod{
+        switch self {
+        case .register(_),.smsLogin(_),.login(_):
+            return .post
+        case .updatePwd(_):
+            return .put
+        default:
+            return .get
+        }
+    }
+    
+    
+    /// 解析格式
+    var sampleData: Data {
+        return "{}".data(using: String.Encoding.utf8)!
+    }
+    //https://sz.58.com/esf-ajax/activity/ashen_switch/?city_id=4
+    //    let idfv = UIDevice.current.identifierForVendor?.uuidString
+    var param: [String:Any] {
+        // 公共参数
+        //        var params: [String: Any] = ["token": "Gz1qYLXeBW8MZuUfDlr9wsAYuVS1cZFMJY9BbaF842L2gRps747o4w=="]
+        var params: [String: Any] = [:]
+        
+        // 收集参数
+        switch self {
+        case let .sms(phone, type):
+            params["phone"] = phone
+            params["type"] = type.value
+        case let .list(pageNO, pageSize):
+            params["pageNO"] = pageNO
+            params["pageSize"] = pageSize
+        case let .register(para):
+            params += para
+        case let .smsLogin(para):
+            params += para
+            break
+        case let .login(para):
+            params += para
+            break
+        case let .updatePwd(para):
+            params += para
+            break
+        default:
+            break
+        }
+        return params
+    }
+    
+    /// 公共请求头
+    var headers: [String : String]? {
+        return [
+            "devtype": "iOS",
+            "devid": UIDevice().identifierForVendor?.uuidString ?? "unknow",
+            //            "Content - Type": "application/json"
+        ]
+    }
+}
+
+class HttpClient{
+    
+    static func request<T: HandyJSON>(_ target: CommomApi,completionHandler:@escaping ((T, String) -> Void)){
+        let url = target.url
+        let params = target.param
+        Logger.debug("request => ",url)
+        Logger.debug(params)
+        
+        if target.method == .get {
+            HTTP.GET(url,parameters: params,requestSerializer: JSONParameterSerializer()){ response in
+                handleResponse(response, completionHandler: completionHandler)
+            }
+        }
+        else if target.method == .post{
+            HTTP.POST(url,parameters: params,requestSerializer: JSONParameterSerializer()){ response in
+                handleResponse(response, completionHandler: completionHandler)
+            }
+        }
+        else if target.method == .put{
+            HTTP.PUT(url,parameters: params,requestSerializer: JSONParameterSerializer()){ response in
+                handleResponse(response, completionHandler: completionHandler)
+            }
+        }
+    }
+    
+}
+
+extension HttpClient{
+    private static func handleResponse<T: HandyJSON>(_ response: Response, completionHandler: @escaping ((T, String) -> Void)) {
+        DispatchQueue.main.sync {
+            guard let dict = receiveData(response) else{
+                completionHandler(T(),"请求失败")
+                return
+            }
+            let responseStr = response.data.string ?? ""
+            if let res = T.deserialize(from: dict){
+                completionHandler(res,responseStr)
+                return
+            }
+            completionHandler(T(),"请求失败")
+        }
+    }
+    
+    static func receiveData(_ response:Response) ->(NSDictionary?){
+        if let _ = response.error {
+            Logger.debug(response.description)
+            return (nil)
+        }
+        Logger.debug(response.data.string ?? "")
+        guard let dict = response.data.jsonObject() as? NSDictionary else{
+            return (nil)
+        }
+        return (dict)
+    }
+    
+    static func receiveData(_ data:Data?, _ response:URLResponse?,_ error:Error?) ->(NSDictionary?){
+        if let _ = error {
+            Logger.debug(response?.description ?? "")
+            return (nil)
+        }
+        
+        guard let data = data,let dict = data.jsonObject() as? NSDictionary else{
+            Logger.debug(response?.description ?? "")
+            return (nil)
+        }
+        Logger.debug(data.string ?? "")
+        return (dict)
+    }
+    
+}
+
+extension Data {
+    var string: String? { String(data: self, encoding: .utf8) }
+    func jsonObject(options: JSONSerialization.ReadingOptions = [.mutableContainers]) -> Any? {
+        do { return try JSONSerialization.jsonObject(with: self, options: options) }
+        catch { Logger.debug(error) }
+        return nil
+    }
+}
+
+extension String {
+    var data: Data? { self.data(using: .utf8) }
+}
index e887c13..8f98274 100644 (file)
@@ -12,7 +12,7 @@ public class ResponseBaseModel: HandyJSON {
 
     public var code = -1
     public var msg = "请求失败"
-    
+    public var success = false
 
-    public var isSuccess: Bool { return code == 20000}
+    public var isSuccess: Bool { return success}
 }
index e31de9e..8d34e7e 100644 (file)
                <string>bluetooth-central</string>
                <string>audio</string>
        </array>
+       <key>NSAppTransportSecurity</key>
+       <dict>
+               <key>NSAllowsArbitraryLoads</key>
+               <true/>
+       </dict>
 </dict>
 </plist>
diff --git a/ShenYin/ShenYin/Tools/AESEncryption.swift b/ShenYin/ShenYin/Tools/AESEncryption.swift
new file mode 100644 (file)
index 0000000..df13e2a
--- /dev/null
@@ -0,0 +1,55 @@
+//
+//  Untitled.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/9/28.
+//
+import CryptoSwift
+
+class AESEncryption {
+    static func encrypt(_ plainText: String, key: String, iv: String) -> String? {
+        guard let plainData = plainText.data(using:.utf8),
+              let keyData = key.data(using:.utf8),
+              let ivData = iv.data(using:.utf8) else {
+            return nil
+        }
+        let plainBytes = Array(plainData.withUnsafeBytes { Array($0) })
+        let keyBytes = Array(keyData.withUnsafeBytes { Array($0) })
+        let ivBytes = Array(ivData.withUnsafeBytes { Array($0) })
+        do {
+            let encrypted = try AES(key: keyBytes, blockMode: CBC(iv: ivBytes), padding:.pkcs5).encrypt(plainBytes)
+            return Data(encrypted).base64EncodedString()
+        } catch {
+            print("Encryption error: \(error)")
+            return nil
+        }
+    }
+    
+    static func decrypt(_ encryptedText: String, key: String, iv: String) -> String? {
+        guard let encryptedData = Data(base64Encoded: encryptedText),
+              let keyData = key.data(using:.utf8),
+              let ivData = iv.data(using:.utf8) else {
+            return nil
+        }
+        let encryptedBytes = Array(encryptedData.withUnsafeBytes { Array($0) })
+        let keyBytes = Array(keyData.withUnsafeBytes { Array($0) })
+        let ivBytes = Array(ivData.withUnsafeBytes { Array($0) })
+        do {
+            let decrypted = try AES(key: keyBytes, blockMode: CBC(iv: ivBytes), padding:.pkcs5).decrypt(encryptedBytes)
+            return String(data: Data(decrypted), encoding:.utf8)
+        } catch {
+            print("Decryption error: \(error)")
+            return nil
+        }
+    }
+    
+    static func generateRandomString(length: Int = 16) -> String {
+        let letters = "abcdef0123456789"
+        var randomString: String = ""
+        for _ in 0..<length {
+            let randomValue = Int.random(in: 0...letters.count - 1)
+            randomString.append(letters[letters.index(letters.startIndex, offsetBy: randomValue)])
+        }
+        return randomString
+    }
+}
index 41598e2..97c8008 100644 (file)
@@ -34,6 +34,7 @@ extension Date {
         /// e.g. 2020-01-01
         static let short: Format = .init("yyyy-MM-dd")
         
+        
         /// e.g. 2020年01月01日
         static let short1: Format = .init("yyy年MM月dd日")
         
@@ -56,6 +57,7 @@ extension Date {
         static let full1: Format = .init("yyyy年MM月dd日 HH:mm:ss.SSS")
         static let full2: Format = .init("yyyy-MM-ddTHH:mm")
         static let full3: Format = .init("yyyy-MM-dd HH:mm")
+        static let full4: Format = .init("yyyyMMdd00000000")
     }
     
     /// 格式化成本地时间
index 1670a5b..60f1899 100644 (file)
 ////  Created by apple on 2020/11/14.
 ////
 //
-//import UIKit
-//import WebKit
-//
-//struct BaseWebViewParams {
-//    var urlStr: String
-//    var request: URLRequest?
-//    var title: String
-//    init(urlStr: String, title: String = "") {
-//        self.urlStr = urlStr
-//        self.title = title
-//        self.request = nil
-//    }
-//    init(request: URLRequest, title: String = "") {
-//        self.request = request
-//        self.urlStr = ""
-//        self.title = title
-//    }
-//}
-//
-//final class BaseWebViewController: BaseVC {
-//    let webView = BaseWebView()
-//    private let progressView = UIProgressView(progressViewStyle: .default)
-//    private var initData: BaseWebViewParams
-//    init(initData: BaseWebViewParams) {
-//        
-//        self.initData = initData
-//        super.init()
-//    }
-//    
-//    
-//    deinit {
-//        webView.removeObserver(self, forKeyPath: "canGoBack")
-//        webView.removeObserver(self, forKeyPath: "estimatedProgress")
-//        webView.removeObserver(self, forKeyPath: "title")
-//    }
-//    override func viewDidLoad() {
-//        super.viewDidLoad()
-//        setupLayout()
-//        setupStyle()
-//    }
-//
-//    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
-//        if keyPath == "canGoBack" {
-//            if webView.canGoBack {
-//                self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
-//            } else {
-//                self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
-//            }
-//        } else if keyPath == "estimatedProgress" {
-//            progressView.alpha = CGFloat(1.0 - webView.estimatedProgress) + 0.2
-//            let progerss = max(Float(webView.estimatedProgress), 0.1)
-//            progressView.setProgress(progerss, animated: true)
-//            if webView.estimatedProgress >= 1.0 {
-//                kDelay(0.3) {
-//                    self.progressView.alpha = 0.0
-//                    self.progressView.setProgress(0.0, animated: false)
-//                }
-//            }
-//        }else if keyPath == "title" {
-//            if initData.title.isEmpty {
-//                navBar.title = webView.title ?? " "
-//            }
-//            
-//        } else {
-//            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
-//        }
-//    }
-//}
-//
-//// MARK: - ********* webview delegate
-//extension BaseWebViewController: WKNavigationDelegate {
-//    
-//    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
-//        webView.evaluateJavaScript(
-//            "document.getElementsByTagName('body')[0].style.background='#FAFAFA'",
-//            completionHandler: nil)
-//    }
-//    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
-//        k_log("==== error: \(error)")
-//    }
-//    func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
-//        k_log("==== error: \(error)")
-//    }
-//    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
-//        if (navigationAction.targetFrame == nil) {
-//            webView.load(navigationAction.request)
-//        }
-//        guard let policy = WKNavigationActionPolicy(rawValue: WKNavigationActionPolicy.allow.rawValue + 2)
-//            else { decisionHandler(.allow); return }
-//        decisionHandler(policy)
-//    }
-//}
-//
-//// MARK: - ********* UIResponse Funcs
-//extension BaseWebViewController {
-//    override func navleftViewTapped() {
-//        if webView.canGoBack {
-//            webView.goBack()
-//        }
-//        else{
-//            if let _ = self.presentingViewController {
-//                self.dismiss(animated: true, completion: nil)
-//            } else if let nav = self.navigationController {
-//                nav.popViewController(animated: true)
-//            }
-//        }
-//
-//    }
-//    override func navRightViewTapped() {
-//        if let _ = self.presentingViewController {
-//            self.dismiss(animated: true, completion: nil)
-//        } else if let nav = self.navigationController {
-//            nav.popViewController(animated: true)
-//        }
-//    }
-//}
-//
-//// MARK: - UI Setup
-//private extension BaseWebViewController {
-//    
-//    func setupLayout() {
-//        view.addSubview(webView)
-//        webView.addSubview(progressView)
-//    
-//        webView.snp.makeConstraints { (m) in
-//            m.top.equalToSuperview().inset(kNavBtm)
-//            m.bottom.equalToSuperview()
-//            m.leading.trailing.equalToSuperview()
-//        }
-//        progressView.snp.makeConstraints { (m) in
-//            m.top.leading.trailing.equalToSuperview()
-//            m.height.equalTo(2)
-//        }
-//    }
-//    
-//    func setupStyle() {
-//        
-//        navBar.title = initData.title
-//        setNavRight(title: "关闭",norColor: .color(.secondary))
-//        
-//        view.backgroundColor = .color(.viewBg)
-//        
-//        webView.navigationDelegate = self
-//        webView.uiDelegate = self
-//        webView.backgroundColor = .color(.viewBg)
-//
-//        webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
-//        webView.addObserver(self, forKeyPath: "canGoBack", options: .new, context: nil)
-//        webView.addObserver(self, forKeyPath: "title", options: .new, context: nil)
-//        webView.evaluateJavaScript("document.getElementsByTagName('body')[0].style.background='#FAFAFA'", completionHandler: nil)
-//        
-//        /// 加载URL
-//        if let requestUrl = initData.urlStr.yc.asUrl {
-//            let request = URLRequest(url: requestUrl)
-//            webView.load(request)
-//        }
-//        else if let request = initData.request {
-//            webView.load(request)
-//        }
-//        else { return }
-//        
-//        /// 进度条
-//        progressView.trackTintColor = .clear
-//        progressView.progressTintColor = .color(.theme)
-//        progressView.isHidden = false
-//        progressView.setProgress(0.05, animated: true)
-//        
-//    }
-//}
-//
-//// MARK: - WKUIDelegate 不实现该代理方法 网页内调用弹窗时会抛出异常,导致程序崩溃
-//extension BaseWebViewController: WKUIDelegate {
-//    // 获取js 里面的提示
-//    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
-//        if self.navigationController?.visibleViewController != self {
-//            completionHandler()
-//            return
-//        }
-//        let alert = UIAlertController(title: "提示", message: message, preferredStyle: .alert)
-//        alert.addAction(UIAlertAction(title: "确定", style: .default, handler: { (_) -> Void in
-//            completionHandler()
-//        }))
-//        alert.addAction(UIAlertAction(title: "取消", style: .cancel, handler: { (_) -> Void in
-//            completionHandler()
-//        }))
-//        self.present(alert, animated: true, completion: nil)
-//    }
-//    
-//    // js 信息的交流
-//    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
-//        let alert = UIAlertController(title: "提示", message: message, preferredStyle: .alert)
-//        alert.addAction(UIAlertAction(title: "确定", style: .default, handler: { (_) -> Void in
-//            completionHandler(true)
-//        }))
-//        alert.addAction(UIAlertAction(title: "取消", style: .cancel, handler: { (_) -> Void in
-//            completionHandler(false)
-//        }))
-//        self.present(alert, animated: true, completion: nil)
-//    }
-//    
-//    // 交互。可输入的文本。
-//    func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
-//        let alert = UIAlertController(title: prompt, message: defaultText, preferredStyle: .alert)
-//        alert.addTextField { (textField: UITextField) -> Void in
-//            textField.textColor = UIColor.red
-//        }
-//        alert.addAction(UIAlertAction(title: "确定", style: .default, handler: { (_) -> Void in
-//            completionHandler(alert.textFields![0].text!)
-//        }))
-//        self.present(alert, animated: true, completion: nil)
-//    }
-//}
-//
+import UIKit
+import WebKit
+
+struct BaseWebViewParams {
+    var urlStr: String
+    var request: URLRequest?
+    var title: String
+    init(urlStr: String, title: String = "") {
+        self.urlStr = urlStr
+        self.title = title
+        self.request = nil
+    }
+    init(request: URLRequest, title: String = "") {
+        self.request = request
+        self.urlStr = ""
+        self.title = title
+    }
+}
+
+final class BaseWebViewController: BaseVC {
+    let webView = BaseWebView()
+    private let progressView = UIProgressView(progressViewStyle: .default)
+    private var initData: BaseWebViewParams
+    init(initData: BaseWebViewParams) {
+        
+        self.initData = initData
+        super.init()
+    }
+    
+    override func setupViews() {
+        view.addSubview(webView)
+        webView.addSubview(progressView)
+        
+        navBar.title = initData.title
+        navBar.rightView.setTitle("关闭", for: .normal)
+        
+        view.backgroundColor = .color(.bgColor)
+        
+        webView.navigationDelegate = self
+        webView.uiDelegate = self
+        webView.backgroundColor = .color(.bgColor)
+        
+        webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
+        webView.addObserver(self, forKeyPath: "canGoBack", options: .new, context: nil)
+        webView.addObserver(self, forKeyPath: "title", options: .new, context: nil)
+        webView.evaluateJavaScript("document.getElementsByTagName('body')[0].style.background='#FAFAFA'", completionHandler: nil)
+        
+        /// 加载URL
+        if let requestUrl = initData.urlStr.yc.asUrl {
+            let request = URLRequest(url: requestUrl)
+            webView.load(request)
+        }
+        else if let request = initData.request {
+            webView.load(request)
+        }
+        else { return }
+        
+        /// 进度条
+        progressView.trackTintColor = .clear
+        progressView.progressTintColor = .color(.theme)
+        progressView.isHidden = false
+        progressView.setProgress(0.05, animated: true)
+    }
+    override func setupLayout() {
+        webView.snp.makeConstraints { (m) in
+            m.top.equalToSuperview().inset(kNavBtm)
+            m.bottom.equalToSuperview()
+            m.leading.trailing.equalToSuperview()
+        }
+        progressView.snp.makeConstraints { (m) in
+            m.top.leading.trailing.equalToSuperview()
+            m.height.equalTo(2)
+        }
+    }
+    
+    override func navleftViewTapped() {
+        if webView.canGoBack {
+            webView.goBack()
+        }
+        else{
+            if let _ = self.presentingViewController {
+                self.dismiss(animated: true, completion: nil)
+            } else if let nav = self.navigationController {
+                nav.popViewController(animated: true)
+            }
+        }
+        
+    }
+    override func navRightViewTapped() {
+        if let _ = self.presentingViewController {
+            self.dismiss(animated: true, completion: nil)
+        } else if let nav = self.navigationController {
+            nav.popViewController(animated: true)
+        }
+    }
+    
+    deinit {
+        webView.removeObserver(self, forKeyPath: "canGoBack")
+        webView.removeObserver(self, forKeyPath: "estimatedProgress")
+        webView.removeObserver(self, forKeyPath: "title")
+    }
+
+    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
+        if keyPath == "canGoBack" {
+            if webView.canGoBack {
+                self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
+            } else {
+                self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
+            }
+        } else if keyPath == "estimatedProgress" {
+            progressView.alpha = CGFloat(1.0 - webView.estimatedProgress) + 0.2
+            let progerss = max(Float(webView.estimatedProgress), 0.1)
+            progressView.setProgress(progerss, animated: true)
+            if webView.estimatedProgress >= 1.0 {
+                kDelay(0.3) {
+                    self.progressView.alpha = 0.0
+                    self.progressView.setProgress(0.0, animated: false)
+                }
+            }
+        }else if keyPath == "title" {
+            if initData.title.isEmpty {
+                navBar.title = webView.title ?? " "
+            }
+            
+        } else {
+            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
+        }
+    }
+}
+
+// MARK: - ********* webview delegate
+extension BaseWebViewController: WKNavigationDelegate {
+    
+    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+        webView.evaluateJavaScript(
+            "document.getElementsByTagName('body')[0].style.background='#FAFAFA'",
+            completionHandler: nil)
+    }
+    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
+        k_log("==== error: \(error)")
+    }
+    func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
+        k_log("==== error: \(error)")
+    }
+    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+        if (navigationAction.targetFrame == nil) {
+            webView.load(navigationAction.request)
+        }
+        guard let policy = WKNavigationActionPolicy(rawValue: WKNavigationActionPolicy.allow.rawValue + 2)
+            else { decisionHandler(.allow); return }
+        decisionHandler(policy)
+    }
+}
+
+// MARK: - WKUIDelegate 不实现该代理方法 网页内调用弹窗时会抛出异常,导致程序崩溃
+extension BaseWebViewController: WKUIDelegate {
+    // 获取js 里面的提示
+    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
+        if self.navigationController?.visibleViewController != self {
+            completionHandler()
+            return
+        }
+        let alert = UIAlertController(title: "提示", message: message, preferredStyle: .alert)
+        alert.addAction(UIAlertAction(title: "确定", style: .default, handler: { (_) -> Void in
+            completionHandler()
+        }))
+        alert.addAction(UIAlertAction(title: "取消", style: .cancel, handler: { (_) -> Void in
+            completionHandler()
+        }))
+        self.present(alert, animated: true, completion: nil)
+    }
+    
+    // js 信息的交流
+    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
+        let alert = UIAlertController(title: "提示", message: message, preferredStyle: .alert)
+        alert.addAction(UIAlertAction(title: "确定", style: .default, handler: { (_) -> Void in
+            completionHandler(true)
+        }))
+        alert.addAction(UIAlertAction(title: "取消", style: .cancel, handler: { (_) -> Void in
+            completionHandler(false)
+        }))
+        self.present(alert, animated: true, completion: nil)
+    }
+    
+    // 交互。可输入的文本。
+    func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
+        let alert = UIAlertController(title: prompt, message: defaultText, preferredStyle: .alert)
+        alert.addTextField { (textField: UITextField) -> Void in
+            textField.textColor = UIColor.red
+        }
+        alert.addAction(UIAlertAction(title: "确定", style: .default, handler: { (_) -> Void in
+            completionHandler(alert.textFields![0].text!)
+        }))
+        self.present(alert, animated: true, completion: nil)
+    }
+}
+
index fa414f1..f849162 100644 (file)
@@ -73,6 +73,11 @@ class SelectCell: BaseTableViewCell {
             titleLabel.text = genderType.title
         }
     }
+    var country:CountryModel = CountryModel(){
+        didSet{
+            titleLabel.text = country.name
+        }
+    }
     
     override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
         super.init(style: style, reuseIdentifier: reuseIdentifier)
index 50f4af0..635358a 100644 (file)
@@ -24,12 +24,19 @@ class AppLaunch:NSObject{
         IQKeyboardManager.shared.resignOnTouchOutside = true
     }
     static func loadCache(){
+        if CacheManager.shared.user.isLogin {
+            App.shared.didLogin()
+        }
+        else{
+            App.shared.goToLogin()
+        }
+        
         let list = DeviceCache.shared.getDeviceList()
         Logger.debug("缓存设备",list)
-        if list.count > 0 {
-            CacheManager.shared.currentDevice = list.first
-            BleManager.shared.startScanDevice()
-            App.shared.enter()
-        }
+//        if list.count > 0 {
+//            CacheManager.shared.currentDevice = list.first
+//            BleManager.shared.startScanDevice()
+//            App.shared.enter()
+//        }
     }
 }
index 183d48a..500ac80 100644 (file)
@@ -17,15 +17,20 @@ class App{
         return getTopViewController(from: vc)
     }
     
-    func enter(){
+    func didLogin(){
         window?.rootViewController = BaseTC()
     }
+    func goToLogin(){
+        window?.rootViewController = BaseNC(rootViewController: LoginVC())
+    }
     
     func goToPage(_ viewController: UIViewController,animated:Bool = true){
         viewController.hidesBottomBarWhenPushed = true
         topVC().navigationController?.pushViewController(viewController, animated: animated)
     }
-    
+    func popPage(){
+        topVC().navigationController?.popViewController(animated: true)
+    }
     
 }
 extension App{
@@ -48,3 +53,5 @@ extension App{
 //        return !BleManager.shared.isConnetd
     }
 }
+
+
index 6f3c078..2e6ef0e 100644 (file)
@@ -199,7 +199,7 @@ extension BleManager : JoyGemAIManagerDelegate {
         self.isConnetd = true
         NotificationManager.post(type: .deviceValidChange, objtect: nil)
         ConnectPopView().showAutoHide {
-            App.shared.enter()
+//            App.shared.enter()
         }
         CacheManager.shared.saveDevice(DeviceModel(name: connectPeripheral?.name ?? "", uuid: connectPeripheral?.identifier.uuidString ?? ""))
         PcmSound.sharedInstance().setup(16000, withchannels: 1)
index 531b766..fa3008b 100644 (file)
@@ -8,10 +8,8 @@
 import UIKit
 
 class SceneDelegate: UIResponder, UIWindowSceneDelegate {
-    
     var window: UIWindow?
 
-
     func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
         // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
         // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
@@ -20,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
         
         App.shared.window = window
         window?.rootViewController = BaseTC()
-//        window?.rootViewController = UINavigationController(rootViewController: ChatVC())
+        window?.rootViewController = UINavigationController(rootViewController: LoginVC())
         AppLaunch.start()
     }
 
diff --git a/ShenYin/ShenYin/UI/Mine/Login/Controller/ContryListVC.swift b/ShenYin/ShenYin/UI/Mine/Login/Controller/ContryListVC.swift
new file mode 100644 (file)
index 0000000..e3cfc89
--- /dev/null
@@ -0,0 +1,73 @@
+//
+//  ContryListVC.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/9/27.
+//
+
+import UIKit
+class ContryListVC: BaseVC{
+    let tableView = UITableView(frame: .zero, style: .plain)
+    var currentType = CountryModel()
+    var didSelected:((_ model:CountryModel)->Void)?
+    
+    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.register(SelectCell.self, forCellReuseIdentifier: SelectCell.reuseID)
+        }
+        view.addSubviews([tableView])
+        
+    }
+    override func setupLayout() {
+        tableView.snp.makeConstraints { make in
+            make.left.right.bottom.equalToSuperview()
+            make.top.equalTo(kNavBtm)
+        }
+    }
+    override func setupData() {
+        LoginVM.fetchCountryList {[weak self] result in
+            guard let self = self else{return}
+            self.tableView.reloadData()
+        }
+    }
+}
+
+extension ContryListVC:UITableViewDelegate,UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return LoginVM.shared.countryList.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = SelectCell.cell(withTableView: tableView, indexPath: indexPath)
+        cell.country = LoginVM.shared.countryList[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 == LoginVM.shared.countryList.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.country.name != currentType.name
+        return cell
+    }
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let data = LoginVM.shared.countryList[indexPath.row]
+        currentType = data
+        tableView.reloadData()
+        didSelected?(data)
+        navigationController?.popViewController(animated: true)
+    }
+}
index 8a5d553..275adcb 100644 (file)
@@ -110,30 +110,46 @@ extension ForgetVC{
             return
         }
         YLprogressHUD.showLoading()
-        LoginVM.sendCode(phone: text) { [weak self] (model) in
+        HttpClient.request(CommomApi.sms(phone: text,type: .forget)) {[weak self] (model:ResponseBaseModel, res) in
             if model.isSuccess{
                 self?.codeView.codeButton.start()
             }
             YLprogressHUD.showAutoHiden(model.msg)
         }
     }
-    @objc func registerAction(){
-        App.shared.goToPage(RegisterVC())
-    }
     @objc func loginAction(){
-        guard let pohne = phoneView.textField.text else{
+        guard let phone = phoneView.textField.value else{
             YLprogressHUD.showAutoHiden("请输入正确的手机号码")
             return
         }
-        guard let  code = codeView.textField.text else{
-            YLprogressHUD.showAutoHiden("请输入正确的验证码")
+        guard let code = codeView.textField.value else{
+            YLprogressHUD.showAutoHiden("请输入验证码")
+            return
+        }
+        guard let pwd = pwdView.textField.value else{
+            YLprogressHUD.showAutoHiden("请输入密码")
             return
         }
         YLprogressHUD.showLoading()
-        LoginVM.login(phone: pohne, code: code) {[weak self] result in
+        
+        let plainText = pwd
+        let key = Date().format(.full4)
+        let iv = AESEncryption.generateRandomString()
+        guard let encryptedText = AESEncryption.encrypt(plainText, key: key, iv: iv) else {
+            return
+        }
+
+        var params:[String:Any] = [:]
+        params["phone"] = phone
+        params["password"] = encryptedText
+        params["nonce"] = iv
+        params["smsCode"] = code
+        
+        let api = CommomApi.updatePwd(param: params)
+        HttpClient.request(api) { (result:ResponseBaseModel,res) in
             if result.isSuccess{
                 YLprogressHUD.showSuccess {
-                    self?.view.window?.rootViewController = BaseTC()
+                    App.shared.popPage()
                 }
             }
             YLprogressHUD.showAutoHiden(result.msg)
index 0b9c891..6748b47 100644 (file)
@@ -6,6 +6,7 @@
 //
 
 import UIKit
+import DeviceKit
 
 class LoginVC:BaseVC{
     
@@ -15,8 +16,7 @@ class LoginVC:BaseVC{
     let phoneView = CustomInputView()
     let pwdView = PasswordView()
     let loginBtn = CustomButton()
-    let registerBtn = CustomButton()
-    let forgetBtn = UIButton()
+    let othersView = OthersLoginView()
     let agreeView  = AgreeView()
     
     override func viewDidLoad() {
@@ -56,15 +56,8 @@ class LoginVC:BaseVC{
         }
         
         loginBtn.set(title: "登录",style: .theme1, target: self, action: #selector(loginAction))
-        registerBtn.set(title: "注册", style: .theme2,target: self, action: #selector(registerAction))
-        forgetBtn.do { v in
-            v.setTitle("忘记密码", for: .normal)
-            v.titleLabel?.font = .regular(12)
-            v.setTitleColor(.hexString("#666666"), for: .normal)
-            v.addTarget(self, action: #selector(forgetAction), for: .touchUpInside)
-        }
         view.addSubviews([tipsLabel,bgView])
-        bgView.addSubviews([titleLabel,phoneView,pwdView,loginBtn,registerBtn,agreeView,forgetBtn])
+        bgView.addSubviews([titleLabel,phoneView,pwdView,loginBtn,agreeView,othersView])
         
     }
     override func setupLayout(){
@@ -96,48 +89,63 @@ class LoginVC:BaseVC{
             make.left.right.equalToSuperview().inset(51)
             make.height.equalTo(55)
         }
-        registerBtn.snp.makeConstraints { make in
-            make.top.equalTo(loginBtn.snp.bottom).offset(20)
-            make.left.right.equalToSuperview().inset(51)
-            make.height.equalTo(55)
-        }
         agreeView.snp.makeConstraints { make in
-            make.bottom.equalToSuperview().inset(62)
+            make.top.equalTo(pwdView.snp.bottom).offset(10)
             make.centerX.equalToSuperview()
             make.height.equalTo(30)
         }
-        forgetBtn.snp.makeConstraints { make in
-            make.right.equalTo(pwdView.snp.right)
-            make.width.equalTo(55)
+        othersView.snp.makeConstraints { make in
+            make.centerX.equalToSuperview()
             make.height.equalTo(50)
-            make.top.equalTo(pwdView.snp.bottom)
+            make.left.right.equalToSuperview()
+            make.top.equalTo(loginBtn.snp.bottom).offset(20)
         }
     }
 
 }
 
 extension LoginVC{
-    @objc func forgetAction(){
-        App.shared.goToPage(ForgetVC())
-    }
 
-    @objc func registerAction(){
-        App.shared.goToPage(RegisterVC())
-    }
     @objc func loginAction(){
         guard let phone = phoneView.textField.value else{
             YLprogressHUD.showAutoHiden("请输入正确的手机号码")
             return
         }
-        guard let code = pwdView.textField.value else{
+        guard let pwd = pwdView.textField.value else{
             YLprogressHUD.showAutoHiden("请输入密码")
             return
         }
         YLprogressHUD.showLoading()
-        LoginVM.login(phone: phone, code: code) {[weak self] result in
+        
+        
+        let plainText = pwd
+        let key = Date().format(.full4)
+        let iv = AESEncryption.generateRandomString()
+        
+        Logger.debug("pwd: \(pwd)")
+        Logger.debug("iv: \(iv)")
+        Logger.debug("key: \(key)")
+        
+        guard let encryptedText = AESEncryption.encrypt(plainText, key: key, iv: iv) else {
+            return
+        }
+        Logger.debug("Encrypted: \(encryptedText)")
+        //        if let decryptedText = AESEncryption.decrypt(encryptedText, key: key, iv: iv) {
+        //            Logger.debug("Decrypted: \(decryptedText)")
+        //        }
+        
+        var params:[String:Any] = [:]
+        params["account"] = phone
+        params["password"] = encryptedText
+        params["nonce"] = iv
+        
+        params["deviceName"] = Device.current.name
+        params["deviceCode"] = CacheManager.uuid()
+
+        LoginVM.login(param: params) { result in
             if result.isSuccess{
                 YLprogressHUD.showSuccess {
-                    self?.view.window?.rootViewController = BaseTC()
+                    App.shared.didLogin()
                 }
             }
             YLprogressHUD.showAutoHiden(result.msg)
index 0bacf68..98eef3b 100644 (file)
@@ -1,22 +1,22 @@
 //
-//  LoginVC.swift
+//  RegisterVC.swift
 //  ShenYin
 //
 //  Created by liangshi on 2025/7/30.
 //
 
 import UIKit
+import DeviceKit
 
 class RegisterVC:BaseVC{
-    let scrollView = UIScrollView()
+    
     let tipsLabel = UILabel()
     let bgView = UIView()
     let titleLabel = UILabel()
+    let regionView = RegionView()
     let phoneView = CustomInputView()
     let codeView = CodeView()
     let pwdView = PasswordView()
-    let pwdView2 = PasswordView()
-    let loginBtn = CustomButton()
     let registerBtn = CustomButton()
     let agreeView  = AgreeView()
     
@@ -24,7 +24,8 @@ class RegisterVC:BaseVC{
         super.viewDidLoad()
     }
     override func setupViews(){
-        self.navBar.title = ""
+        //        self.navBar.title = ""
+        //        navBar.isHidden = true
         view.backgroundColor = .color(.bgColor)
         tipsLabel.do { v in
             v.numberOfLines = 0
@@ -48,45 +49,35 @@ class RegisterVC:BaseVC{
             v.font = .medium(18)
             v.textColor = .hexString("#121212")
         }
-        phoneView.do { v in
-            v.textField.text = "13480939445"
-        }
+
         codeView.do { v in
             v.codeButton.addTarget(self, action: #selector(codeButtonAction), for: .touchUpInside)
         }
-        pwdView.do { v in
-            v.textField.placeholder = "请输入密码"
+        regionView.do { v in
+            v.didSelected = {[weak self] model in
+                guard let self = self else {return}
+                updateLayout()
+                phoneView.showLeftView = model.isChinese
+            }
+            v.region = CountryModel.chinese()
         }
-        pwdView2.do { v in
-            v.textField.placeholder = "请确认密码"
+        phoneView.do { v in
+            v.textField.text = "19276672101"
         }
         registerBtn.set(title: "注册",style: .theme1, target: self, action: #selector(registerAction))
-        loginBtn.set(title: "登录",style: .theme2, target: self, action: #selector(loginAction))
-        agreeView.isHidden = true
-        
-        scrollView.backgroundColor = .clear
-        view.addSubview(scrollView)
-        scrollView.addSubviews([tipsLabel,bgView])
-        bgView.addSubviews([titleLabel,phoneView,codeView,pwdView,pwdView2,loginBtn,registerBtn,agreeView])
+        view.addSubviews([tipsLabel,bgView])
+        bgView.addSubviews([titleLabel,regionView,phoneView,codeView,pwdView,registerBtn,agreeView])
         
     }
     override func setupLayout(){
-        scrollView.snp.makeConstraints { make in
-            make.top.equalToSuperview().inset(kNavBtm)
-            make.left.equalToSuperview()
-            make.height.equalTo(kScreenHei - kNavBtm)
-            make.width.equalTo(kScreenWid)
-        }
         tipsLabel.snp.makeConstraints { make in
             make.left.equalToSuperview().inset(28)
-            make.top.equalTo(20)
+            make.top.equalTo(kNavBtm + 20)
             make.centerX.equalToSuperview()
         }
         bgView.snp.makeConstraints { make in
-            make.left.equalToSuperview()
-            make.top.equalToSuperview().inset(160)
-            make.width.equalTo(kScreenWid)
-            make.height.equalTo(kScreenHei - 250)
+            make.left.right.equalToSuperview()
+            make.top.equalToSuperview().inset(244)
             make.bottom.equalToSuperview()
         }
         
@@ -94,10 +85,14 @@ class RegisterVC:BaseVC{
             make.top.equalToSuperview().offset(34)
             make.left.right.equalToSuperview().inset(36)
         }
-        phoneView.snp.makeConstraints { make in
+        regionView.snp.makeConstraints { make in
             make.top.equalToSuperview().offset(82)
             make.left.right.equalToSuperview().inset(36)
         }
+        phoneView.snp.makeConstraints { make in
+            make.top.equalTo(regionView.snp.bottom).offset(20)
+            make.left.right.equalToSuperview().inset(36)
+        }
         codeView.snp.makeConstraints { make in
             make.top.equalTo(phoneView.snp.bottom).offset(20)
             make.left.right.equalToSuperview().inset(36)
@@ -106,27 +101,25 @@ class RegisterVC:BaseVC{
             make.top.equalTo(codeView.snp.bottom).offset(20)
             make.left.right.equalToSuperview().inset(36)
         }
-        pwdView2.snp.makeConstraints { make in
-            make.top.equalTo(pwdView.snp.bottom).offset(20)
-            make.left.right.equalToSuperview().inset(36)
-        }
         registerBtn.snp.makeConstraints { make in
-            make.top.equalTo(pwdView2.snp.bottom).offset(54)
-            make.left.right.equalToSuperview().inset(51)
-            make.height.equalTo(55)
-        }
-        loginBtn.snp.makeConstraints { make in
-            make.top.equalTo(registerBtn.snp.bottom).offset(20)
+            make.top.equalTo(pwdView.snp.bottom).offset(54)
             make.left.right.equalToSuperview().inset(51)
             make.height.equalTo(55)
         }
         agreeView.snp.makeConstraints { make in
-            make.bottom.equalToSuperview().inset(62)
+            make.top.equalTo(pwdView.snp.bottom).offset(10)
             make.centerX.equalToSuperview()
             make.height.equalTo(30)
         }
     }
-    
+    func updateLayout(){
+        if regionView.region.name == "中国"{
+            phoneView.textField.placeholder = "请输入手机号码"
+        }
+        else{
+            phoneView.textField.placeholder = "请输入邮箱"
+        }
+    }
 }
 
 extension RegisterVC{
@@ -136,7 +129,7 @@ extension RegisterVC{
             return
         }
         YLprogressHUD.showLoading()
-        LoginVM.sendCode(phone: text) { [weak self] (model) in
+        HttpClient.request(CommomApi.sms(phone: text,type: .reg)) {[weak self] (model:ResponseBaseModel, res) in
             if model.isSuccess{
                 self?.codeView.codeButton.start()
             }
@@ -156,29 +149,39 @@ extension RegisterVC{
             YLprogressHUD.showAutoHiden("请输入密码")
             return
         }
-        guard let pwd2 = pwdView2.textField.value else{
-            YLprogressHUD.showAutoHiden("请输入确认密码")
+        YLprogressHUD.showLoading()
+                
+        let plainText = pwd
+        let key = Date().format(.full4)
+        let iv = AESEncryption.generateRandomString()
+        
+        Logger.debug("pwd: \(pwd)")
+        Logger.debug("iv: \(iv)")
+        Logger.debug("key: \(key)")
+        
+        guard let encryptedText = AESEncryption.encrypt(plainText, key: key, iv: iv) else {
             return
         }
-        YLprogressHUD.showLoading()
-//        LoginVM.login(phone: pohne, code: code) {[weak self] result in
-//            if result.isSuccess{
-//                YLprogressHUD.showSuccess {
-//                    self?.view.window?.rootViewController = BaseTC()
-//                }
-//            }
-//            YLprogressHUD.showAutoHiden(result.msg)
-//        }
-    }
-    @objc func loginAction(){
-        App.shared.goToPage(LoginVC())
-//        guard let pohne = phoneView.textField.text else{
-//            YLprogressHUD.showAutoHiden("请输入正确的手机号码")
-//            return
-//        }
-//        guard let  code = codeView.textField.text else{
-//            YLprogressHUD.showAutoHiden("请输入正确的验证码")
-//            return
+        Logger.debug("Encrypted: \(encryptedText)")
+//        if let decryptedText = AESEncryption.decrypt(encryptedText, key: key, iv: iv) {
+//            Logger.debug("Decrypted: \(decryptedText)")
 //        }
+        var params:[String:Any] = [:]
+        params["phone"] = phone
+        params["userName"] = Device.current.name
+        params["password"] = encryptedText
+        params["nonce"] = iv
+        params["smsCode"] = code
+        params["countryId"] = regionView.region.id
+        
+        let api = CommomApi.register(param: params)
+        HttpClient.request(api) { (result:ResponseBaseModel,res) in
+            if result.isSuccess{
+                YLprogressHUD.showSuccess {
+                    App.shared.popPage()
+                }
+            }
+            YLprogressHUD.showAutoHiden(result.msg)
+        }
     }
 }
diff --git a/ShenYin/ShenYin/UI/Mine/Login/Controller/SmsLoginVC.swift b/ShenYin/ShenYin/UI/Mine/Login/Controller/SmsLoginVC.swift
new file mode 100644 (file)
index 0000000..940af6f
--- /dev/null
@@ -0,0 +1,168 @@
+//
+//  PwdLoginVC.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/9/27.
+//
+
+import UIKit
+import DeviceKit
+
+class SmsLoginVC:BaseVC{
+    
+    let tipsLabel = UILabel()
+    let bgView = UIView()
+    let titleLabel = UILabel()
+    let regionView = RegionView()
+    let phoneView = CustomInputView()
+    let codeView = CodeView()
+    let loginBtn = CustomButton()
+    let pwdBtn = CustomButton()
+    let agreeView  = AgreeView()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+    }
+    override func setupViews(){
+        view.backgroundColor = .color(.bgColor)
+        tipsLabel.do { v in
+            v.numberOfLines = 0
+            v.font = .medium(22)
+            v.textColor = .rgb(0x121212)
+            let text = "您好, \n欢迎使用DeepChords"
+            let stype = NSMutableParagraphStyle()
+            stype.lineSpacing = 10
+            let attrString = NSMutableAttributedString(string: text)
+            let attr: [NSAttributedString.Key : Any] = [.font: UIFont.medium(22),.paragraphStyle:stype]
+            attrString.addAttributes(attr, range: NSRange(location: 0, length: attrString.length))
+            v.attributedText = attrString
+        }
+        
+        bgView.do { v in
+            v.backgroundColor = .white
+            v.yc.setCorners(withRoundedRect: CGRect(x: 0, y: 0, width: kScreenWid, height: kScreenHei - 244), roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 50, height: 50))
+        }
+        titleLabel.do { v in
+            v.text = "验证码登录"
+            v.font = .medium(18)
+            v.textColor = .hexString("#121212")
+        }
+
+        codeView.do { v in
+            v.codeButton.addTarget(self, action: #selector(codeButtonAction), for: .touchUpInside)
+        }
+        regionView.do { v in
+            v.didSelected = {[weak self] model in
+                guard let self = self else {return}
+                updateLayout()
+                phoneView.showLeftView = model.isChinese
+            }
+            v.region = CountryModel.chinese()
+        }
+        phoneView.do { v in
+            v.textField.text = "13480939445"
+        }
+        loginBtn.set(title: "登录",style: .theme1, target: self, action: #selector(loginAction))
+        pwdBtn.set(title: "密码登录", style: .theme2,target: self, action: #selector(pwdAction))
+        view.addSubviews([tipsLabel,bgView])
+        bgView.addSubviews([titleLabel,regionView,phoneView,codeView,loginBtn,pwdBtn,agreeView])
+        
+    }
+    override func setupLayout(){
+        tipsLabel.snp.makeConstraints { make in
+            make.left.equalToSuperview().inset(28)
+            make.top.equalTo(kNavBtm + 20)
+            make.centerX.equalToSuperview()
+        }
+        bgView.snp.makeConstraints { make in
+            make.left.right.equalToSuperview()
+            make.top.equalToSuperview().inset(244)
+            make.bottom.equalToSuperview()
+        }
+        
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalToSuperview().offset(34)
+            make.left.right.equalToSuperview().inset(36)
+        }
+        regionView.snp.makeConstraints { make in
+            make.top.equalToSuperview().offset(82)
+            make.left.right.equalToSuperview().inset(36)
+        }
+        phoneView.snp.makeConstraints { make in
+            make.top.equalTo(regionView.snp.bottom).offset(20)
+            make.left.right.equalToSuperview().inset(36)
+        }
+        codeView.snp.makeConstraints { make in
+            make.top.equalTo(phoneView.snp.bottom).offset(20)
+            make.left.right.equalToSuperview().inset(36)
+        }
+        loginBtn.snp.makeConstraints { make in
+            make.top.equalTo(codeView.snp.bottom).offset(54)
+            make.left.right.equalToSuperview().inset(51)
+            make.height.equalTo(55)
+        }
+        pwdBtn.snp.makeConstraints { make in
+            make.top.equalTo(loginBtn.snp.bottom).offset(20)
+            make.left.right.equalToSuperview().inset(51)
+            make.height.equalTo(55)
+        }
+        agreeView.snp.makeConstraints { make in
+            make.top.equalTo(codeView.snp.bottom).offset(10)
+            make.centerX.equalToSuperview()
+            make.height.equalTo(30)
+        }
+    }
+    func updateLayout(){
+        if regionView.region.name == "中国"{
+            phoneView.textField.placeholder = "请输入手机号码"
+        }
+        else{
+            phoneView.textField.placeholder = "请输入邮箱"
+        }
+    }
+}
+
+extension SmsLoginVC{
+    @objc func pwdAction(){
+        App.shared.popPage()
+    }
+    @objc func codeButtonAction(){
+        guard let text = phoneView.textField.value else{
+            YLprogressHUD.showAutoHiden("请输入正确的手机号码")
+            return
+        }
+        YLprogressHUD.showLoading()
+        HttpClient.request(CommomApi.sms(phone: text,type: .login)) {[weak self] (model:ResponseBaseModel, res) in
+            if model.isSuccess{
+                self?.codeView.codeButton.start()
+            }
+            YLprogressHUD.showAutoHiden(model.msg)
+        }
+    }
+    @objc func loginAction(){
+        guard let phone = phoneView.textField.value else{
+            YLprogressHUD.showAutoHiden("请输入正确的手机号码")
+            return
+        }
+        guard let code = codeView.textField.value else{
+            YLprogressHUD.showAutoHiden("请输入验证码")
+            return
+        }
+        YLprogressHUD.showLoading()
+        var params:[String:Any] = [:]
+        params["phone"] = phone
+        params["deviceName"] = Device.current.name
+        params["deviceCode"] = CacheManager.uuid()
+        params["smsCode"] = code
+        params["countryId"] = regionView.region.id
+
+        LoginVM.smsLogin(param: params) { result in
+            if result.isSuccess{
+                YLprogressHUD.showSuccess {
+                    App.shared.didLogin()
+                }
+            }
+            YLprogressHUD.showAutoHiden(result.msg)
+        }
+    }
+}
index a7963b3..e3dab41 100644 (file)
@@ -16,8 +16,8 @@ class AgreeView: BaseView {
         agreeBtn.do { (v) in
             v.isSelected = true
             v.contentMode = .center
-            v.setImage(UIImage(named: "select_normal"), for: .normal)
-            v.setImage(UIImage(named: "select_selected"), for: .selected)
+            v.setImage(UIImage(named: "tip_normal"), for: .normal)
+            v.setImage(UIImage(named: "tip_selected"), for: .selected)
             v.addTarget(self, action: #selector(agreeBtnAction), for: .touchUpInside)
         }
         agreeTitle.do { (v) in
@@ -27,10 +27,10 @@ class AgreeView: BaseView {
             v.text = "登录并同意"
         }
         agreeValue.do { (v) in
-            v.textColor = .hexString("#121212")
+            v.textColor = .hexString("#0092E1")
             v.font = .regular(12)
             v.textAlignment = .left
-            v.text = "《隐私权政策》"
+            v.text = " 《隐私政策》"
             v.add(target: self, action: #selector(agreementAction))
         }
         andlabel.do { (v) in
@@ -40,7 +40,7 @@ class AgreeView: BaseView {
             v.text = " 和 "
         }
         useValue.do { (v) in
-            v.textColor = .hexString("#121212")
+            v.textColor = .hexString("#0092E1")
             v.font = .regular(12)
             v.textAlignment = .left
             v.text = "《用户协议》"
@@ -77,12 +77,15 @@ class AgreeView: BaseView {
         agreeBtn.isSelected = !agreeBtn.isSelected
     }
     @objc func agreementAction(){
-        
+        let param = BaseWebViewParams(urlStr: "https://www.baidu.com")
+        let vc = BaseWebViewController(initData: param)
+        App.shared.goToPage(vc)
     }
     @objc func useAction(){
-        
+        let param = BaseWebViewParams(urlStr: "https://www.baidu.com")
+        let vc = BaseWebViewController(initData: param)
+        App.shared.goToPage(vc)
     }
-    
 }
 
 extension UILabel{
index 7e05c1b..85c6b63 100644 (file)
@@ -68,4 +68,15 @@ extension UIView{
             make.centerY.equalToSuperview()
         }
     }
+    func addRightLine(height:Float){
+        let line = UIView()
+        line.backgroundColor = .hexString("#666666")
+        addSubview(line)
+        line.snp.makeConstraints { make in
+            make.height.equalTo(height)
+            make.width.equalTo(1)
+            make.right.equalToSuperview()
+            make.centerY.equalToSuperview()
+        }
+    }
 }
index c639c9e..424a6b0 100644 (file)
@@ -15,6 +15,25 @@ class CustomInputView: BaseView {
             v.textColor = .black
             v.keyboardType = .phonePad
             v.placeholder = "请输入手机号码"
+            
+            let label = UILabel()
+            label.textColor = .hexString("#666666")
+            label.font = .medium(16)
+            let imageAtt = NSTextAttachment()
+            imageAtt.image = UIImage(named: "arrow_next")
+            imageAtt.bounds = CGRect(x: 0, y: 0, width: 8, height: 12)
+            let text = "+86  "
+            let textString = NSAttributedString(string: text)
+            
+            let muAtt = NSMutableAttributedString()
+            muAtt.append(textString)
+            muAtt.append(NSAttributedString(attachment: imageAtt))
+            muAtt.append(NSAttributedString(string: "   "))
+            label.attributedText = muAtt
+            
+//            label.backgroundColor = .red
+            v.leftView = label
+            v.leftViewMode = .never
         }
         
         inputBgView.backgroundColor =  .rgb(0xF3F4F8)
@@ -36,4 +55,18 @@ class CustomInputView: BaseView {
             make.top.bottom.equalToSuperview()
         }
     }
+    
+    var showLeftView:Bool = false{
+        didSet{
+            if showLeftView {
+                textField.leftViewMode = .always
+                textField.text = ""
+            }
+            else{
+                textField.leftViewMode = .never
+                textField.text = ""
+            }
+        }
+    }
 }
+
diff --git a/ShenYin/ShenYin/UI/Mine/Login/View/OthersLoginView.swift b/ShenYin/ShenYin/UI/Mine/Login/View/OthersLoginView.swift
new file mode 100644 (file)
index 0000000..3d8a59e
--- /dev/null
@@ -0,0 +1,84 @@
+//
+//  OthersLoginView.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/9/27.
+//
+
+import UIKit
+class OthersLoginView: BaseView {
+    let registerLabel = UILabel()
+    let pwdLabel = UILabel()
+    let smsLabel = UILabel()
+    let line1 = UIView()
+    let line2 = UIView()
+    
+    override func setupViews() {
+        addSubviews([registerLabel,pwdLabel,smsLabel,line1,line2])
+        line1.backgroundColor = .hexString("#666666")
+        line2.backgroundColor = .hexString("#666666")
+        
+        registerLabel.do { v in
+            v.text = "  注册"
+            v.textColor = .hexString("#666666")
+            v.font = .regular(12)
+            v.textAlignment = .center
+            v.yc.addTap(target: self, action: #selector(registerAction))
+        }
+        pwdLabel.do { v in
+            v.text = "忘记密码"
+            v.textColor = .hexString("#666666")
+            v.font = .regular(12)
+            v.textAlignment = .center
+            v.yc.addTap(target: self, action: #selector(forgetAction))
+        }
+        smsLabel.do { v in
+            v.text = "验证码登录"
+            v.textColor = .hexString("#666666")
+            v.font = .regular(12)
+            v.textAlignment = .center
+            v.yc.addTap(target: self, action: #selector(smsAction))
+        }
+    }
+    override func setupLayout() {
+        pwdLabel.snp.makeConstraints { make in
+//            make.width.equalTo(78)
+            make.height.equalToSuperview()
+            make.center.equalToSuperview()
+        }
+        registerLabel.snp.makeConstraints { make in
+//            make.width.equalTo(78)
+            make.height.equalToSuperview()
+            make.centerY.equalToSuperview()
+            make.right.equalTo(pwdLabel.snp.left).offset(-30)
+        }
+        smsLabel.snp.makeConstraints { make in
+//            make.width.equalTo(78)
+            make.height.equalToSuperview()
+            make.centerY.equalToSuperview()
+            make.left.equalTo(pwdLabel.snp.right).offset(30)
+        }
+        line1.snp.makeConstraints { make in
+            make.height.equalTo(12)
+            make.width.equalTo(1)
+            make.right.equalTo(pwdLabel.snp.left).offset(-15)
+            make.centerY.equalToSuperview()
+        }
+        line2.snp.makeConstraints { make in
+            make.height.equalTo(12)
+            make.width.equalTo(1)
+            make.left.equalTo(pwdLabel.snp.right).offset(15)
+            make.centerY.equalToSuperview()
+        }
+    }
+    @objc func forgetAction(){
+        App.shared.goToPage(ForgetVC())
+    }
+    
+    @objc func registerAction(){
+        App.shared.goToPage(RegisterVC())
+    }
+    @objc func smsAction(){
+        App.shared.goToPage(SmsLoginVC())
+    }
+}
diff --git a/ShenYin/ShenYin/UI/Mine/Login/View/RegionView.swift b/ShenYin/ShenYin/UI/Mine/Login/View/RegionView.swift
new file mode 100644 (file)
index 0000000..603f629
--- /dev/null
@@ -0,0 +1,90 @@
+//
+//  AreaView.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/9/27.
+//
+import UIKit
+
+
+class RegionView: BaseView {
+    let inputBgView = UIView()
+    let titleLabel = UILabel()
+    let valeLabel = UILabel()
+    let arrrowIcon = UIImageView()
+    
+    var didSelected:((_ model:CountryModel)->Void)?
+    var region = CountryModel(){
+        didSet{
+            valeLabel.text = region.name
+            didSelected?(region)
+        }
+    }
+    
+    override func setupViews() {
+        addSubviews([inputBgView])
+        inputBgView.addSubviews([titleLabel,valeLabel,arrrowIcon])
+        
+        inputBgView.backgroundColor =  .rgb(0xF3F4F8)
+        inputBgView.layer.cornerRadius = 10
+        inputBgView.layer.masksToBounds = true
+        inputBgView.yc.addTap(target: self, action: #selector(tapAction))
+        titleLabel.do { v in
+            v.text = "国家/地区"
+            v.textColor = .hexString("#666666")
+            v.font = .medium(16)
+            v.textAlignment = .center
+        }
+        valeLabel.do { v in
+            v.textColor = .hexString("#121212")
+            v.font = .medium(16)
+            v.textAlignment = .center
+        }
+        arrrowIcon.do { v in
+            v.image = UIImage(named: "arrow_next")
+        }
+        
+        loadData()
+    }
+    override func setupLayout() {
+        inputBgView.snp.makeConstraints { make in
+            make.left.right.equalToSuperview()
+            make.top.equalToSuperview()
+            make.height.equalTo(50)
+            make.bottom.equalToSuperview()
+        }
+
+        titleLabel.snp.makeConstraints { make in
+            make.left.equalToSuperview().inset(20)
+            make.centerY.equalToSuperview()
+        }
+        arrrowIcon.snp.makeConstraints { make in
+            make.right.equalToSuperview().inset(20)
+            make.centerY.equalToSuperview()
+            make.width.equalTo(8)
+            make.height.equalTo(12)
+        }
+        valeLabel.snp.makeConstraints { make in
+            make.right.equalTo(arrrowIcon.snp.left).offset(-10)
+            make.centerY.equalToSuperview()
+        }
+    }
+    @objc func tapAction(){
+        let vc = ContryListVC()
+        vc.didSelected = {[weak  self] model in
+            guard let self = self else{return}
+            region = model
+        }
+        App.shared.goToPage(vc)
+    }
+    func loadData(){
+
+        
+        LoginVM.fetchCountryList {[weak self] result in
+            guard let self = self else{return}
+            if result.isSuccess{
+                
+            }
+        }
+    }
+}
diff --git a/ShenYin/ShenYin/UI/Mine/Login/ViewModel/CountryModel.swift b/ShenYin/ShenYin/UI/Mine/Login/ViewModel/CountryModel.swift
new file mode 100644 (file)
index 0000000..88fc5c4
--- /dev/null
@@ -0,0 +1,34 @@
+//
+//  CountryModel.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/9/27.
+//
+
+import Foundation
+import HandyJSON
+public class CountryModelRoot: ResponseBaseModel {
+    public var data = [CountryModel]()
+}
+public class CountryModel: HandyJSON {
+    public var id = ""
+    public var name = ""
+    public var language = ""
+    public var money = ""
+    public var phonePrefix = ""
+    
+    required public init() { }
+    
+    var isChinese:Bool{
+        return name == "中国"
+    }
+    
+    static func chinese()->CountryModel{
+        let model = CountryModel()
+        model.name = "中国"
+        model.phonePrefix = "86"
+        model.id = "1968848875208851458"
+        
+        return model
+    }
+}
index 551f367..81bbaf8 100644 (file)
@@ -32,15 +32,32 @@ class LoginModel:ResponseBaseModel {
 
 
 class LoginVM:BaseVM{
-    static public func sendCode(phone:String, completion:@escaping (_ result:ResponseBaseModel) ->Void) {
-        HttpClient.instance.request(CommomApi.sms(phone: phone,type: .login)) { (model:ResponseBaseModel, res) in
+    static let shared = LoginVM()
+    var countryList = [CountryModel]()
+
+    static public func login(param:[String:Any], completion:@escaping (_ result:LoginModel) ->Void) {
+        let api = CommomApi.login(param: param)
+        HttpClient.request(api) { (model:LoginModel,res) in
+            if model.isSuccess{
+                CacheManager.shared.login(token: model.data)
+            }
             completion(model)
         }
     }
-    static public func login(phone:String,code:String, completion:@escaping (_ result:LoginModel) ->Void) {
-        HttpClient.instance.request(CommomApi.login(phone: phone,code: code)) { (model:LoginModel, res) in
+    static public func smsLogin(param:[String:Any], completion:@escaping (_ result:LoginModel) ->Void) {
+        let api = CommomApi.smsLogin(param: param)
+        HttpClient.request(api) { (model:LoginModel,res) in
+            if model.isSuccess{
+                CacheManager.shared.login(token: model.data)
+            }
+            completion(model)
+        }
+    }
+    
+    static public func fetchCountryList(completion:@escaping (_ result:CountryModelRoot) ->Void) {
+        HttpClient.request(CommomApi.countryList) { (model:CountryModelRoot, res) in
             if model.isSuccess{
-                UserModel.shared.token = model.data
+                LoginVM.shared.countryList = model.data
             }
             completion(model)
         }
index 73a0e75..c580355 100644 (file)
@@ -6,13 +6,38 @@
 //
 
 
-
-
-
 class UserModel {
-    static let shared = UserModel()
-    var token:String?
+    static let cacheKey = "Login"
+    var token:String? {
+        didSet{
+           saveToken(text: token)
+        }
+    }
     var isLogin:Bool{
         return token != nil
     }
+    func loadCache(){
+        self.token = getToken()
+        if self.token != nil {
+        }
+    }
+}
+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()
+        }
+    }
 }
index 3008cda..f456ff3 100644 (file)
@@ -63,13 +63,26 @@ class MoreSetVC: BaseVC{
         }
     }
     @objc func swictAction(){
-        
+        CustomAlert.showWith(title: "提示", messgage: "确定要切换账号吗?", cancelCallback: nil) {
+            CacheManager.shared.logout()
+        }
     }
     @objc func logoutAction(){
-        
+        CustomAlert.showWith(title: "提示", messgage: "确定要退出登录吗?", cancelCallback: nil) {
+            CacheManager.shared.logout()
+        }
     }
     @objc func deactivateAction(){
-        
+        CustomAlert.showWith(title: "提示", messgage: "真的要注销账号吗?", cancelCallback: nil) {
+            HttpClient.request(.deactivate) { (result:ResponseBaseModel,res) in
+                if result.isSuccess{
+                    YLprogressHUD.showSuccess {
+                        CacheManager.shared.logout()
+                    }
+                }
+                YLprogressHUD.showAutoHiden(result.msg)
+            }
+        }
     }
 }
 
index 49d9de8..788934d 100644 (file)
@@ -14,5 +14,48 @@ class CacheManager{
         self.currentDevice = device
         DeviceCache.shared.saveDevice(device)
     }
+    
+    var user = UserModel()
+    
+    init(){
+        user.loadCache()
+        tokenIsValid()
+    }
 }
 
+extension CacheManager{
+    func login(token:String){
+        user.token = token
+        NotificationManager.post(type: .login, objtect: nil)
+        
+    }
+    func logout(){
+        user.token = nil
+        NotificationManager.post(type: .logout, objtect: nil)
+        App.shared.goToLogin()
+    }
+    func tokenIsValid(){
+        HttpClient.request(CommomApi.token) { [weak self](model:ResponseBaseModel,res) in
+            guard let self = self else{return}
+            if model.isSuccess{
+                
+            }
+            else{
+                logout()
+            }
+        }
+    }
+}
+
+
+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
+    }
+}
index dc5ce48..c8958a2 100644 (file)
@@ -11,6 +11,7 @@
 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){
index 50771da..21b1732 100644 (file)
@@ -9,11 +9,17 @@ import Foundation
 
 enum NotificationType:String{
     case deviceValidChange
+    case login
+    case logout
     
     var value:String{
         switch self {
         case .deviceValidChange:
             return "deviceValidChange"
+        case .login:
+            return "login"
+        case .logout:
+            return "logout"
         default:
             return "unknown"
         }
index 426028d..e7c4132 100644 (file)
@@ -24,7 +24,8 @@ target 'ShenYin' do
    pod 'JXSegmentedView'
    pod 'JXPagingView/Paging'
   ##   pod 'Alamofire'
-  pod 'Moya'#网络
+#  pod 'Moya'#网络
+  pod 'SwiftHTTP'
   pod 'swiftScan'#二维码
   pod 'TZImagePickerController'#图片选择
   pod 'netfox'#调试
@@ -32,6 +33,8 @@ target 'ShenYin' do
 
   pod 'SocketRocket'#socket,AI用到
   pod 'DGActivityIndicatorView'#扫描雷达
+  pod 'DeviceKit', '~> 5.4.0'
+  pod 'CryptoSwift'
 #  pod 'MultiSlider'
 #  pod 'DSWaveformImage'