完善设备绑定
authorliangzhenying <867174124@qq.com>
Tue, 21 Oct 2025 10:10:38 +0000 (18:10 +0800)
committerliangzhenying <867174124@qq.com>
Tue, 21 Oct 2025 10:10:38 +0000 (18:10 +0800)
40 files changed:
ShenYin/ShenYin/Api/HttpClient2.swift
ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Contents.json [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Slice 7@2x.png [new file with mode: 0644]
ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Slice 7@3x.png [new file with mode: 0644]
ShenYin/ShenYin/UI/Base/BaseTC.swift
ShenYin/ShenYin/UI/Base/BaseTableViewCell.swift
ShenYin/ShenYin/UI/Base/BaseTableViewSetionHeader.swift
ShenYin/ShenYin/UI/Base/BaseVC.swift
ShenYin/ShenYin/UI/Customer/CustomerVC.swift
ShenYin/ShenYin/UI/Home/Device/DeviceModel.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/Device/DeviceVM.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/Device/HomeDeviceCell.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/Device/HomeDeviceVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/Device/HomeHeadInfoView.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift
ShenYin/ShenYin/UI/Home/Root/HomeHeadView.swift
ShenYin/ShenYin/UI/Home/Scan/AddDeviceVC.swift [moved from ShenYin/ShenYin/UI/Scan/AddDeviceVC.swift with 100% similarity]
ShenYin/ShenYin/UI/Home/Scan/BleScanVC.swift [moved from ShenYin/ShenYin/UI/Scan/BleScanVC.swift with 70% similarity]
ShenYin/ShenYin/UI/Home/Scan/QrVC.swift [moved from ShenYin/ShenYin/UI/Scan/QrVC.swift with 100% similarity]
ShenYin/ShenYin/UI/Launch/AppLaunch.swift
ShenYin/ShenYin/UI/Launch/AppManager.swift
ShenYin/ShenYin/UI/Launch/BleManager.swift
ShenYin/ShenYin/UI/Mine/AccountSetVC.swift
ShenYin/ShenYin/UI/Mine/Device/DeviceInfoVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/Device/MyDeviceInfoVM.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/Device/MyDeviceListVC.swift [moved from ShenYin/ShenYin/UI/Mine/MyDeviceList.swift with 79% similarity]
ShenYin/ShenYin/UI/Mine/DeviceInfoVC.swift [deleted file]
ShenYin/ShenYin/UI/Mine/Feedback/Commit/FeedbackCommitVC.swift
ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginDetailVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginRecordModel.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginVM.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Mine/Root/MineHeaderView.swift
ShenYin/ShenYin/UI/Mine/Root/MineVC.swift
ShenYin/ShenYin/UI/Mine/UserInfo/CustomCell1.swift
ShenYin/ShenYin/UI/Sleep/AudioPlayerVC.swift
ShenYin/ShenYin/UI/common/Cache/CacheManager.swift
ShenYin/ShenYin/UI/common/CommonCell.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/common/CustomButton.swift
ShenYin/ShenYin/UI/common/pop/ConnectPopView.swift

index 4bf340c..d353e49 100644 (file)
@@ -15,6 +15,7 @@ enum RequestMethod{
     case get
     case post
     case put
+    case delete
 }
 
 // 相关api
@@ -67,6 +68,17 @@ enum CommomApi
     case commitFeedback(param:[String:Any])
     /// 获取提交的意见反馈
     case feedbackHistory
+    /// 获取绑定的设备列表
+    case deviceList
+    /// 绑定设备
+    case bindDevice(param:[String:Any])
+    /// 获取登录记录
+    case loginRecord(page:Int)
+    /// 删除登录记录
+    case deleteLoginRecord(recordId:String)
+    
+    /// 上报版本号
+    case otaUpdateVersion( param:[String:Any])
 }
 
 // 实现TargetType协议
@@ -146,7 +158,18 @@ extension CommomApi{
             return "/uc/feedback"
         case .feedbackHistory:
             return "/uc/feedback"
+           
+        case .deviceList:
+            return "/dev/device/list"
+        case .bindDevice(let param):
+            return "/dev/bind?deviceId=\(param["deviceId"]!)&mac=\(param["mac"]!)&prodName=\(param["prodName"]!)"
+        case .loginRecord(let page):
+            return "/uc/login-record/list/\(page)"
+        case .deleteLoginRecord(let recordId):
+            return "/uc/login-record/\(recordId)"
             
+        case .otaUpdateVersion(let param):
+            return "/ota/task/rest"
             
         }
     }
@@ -155,8 +178,10 @@ extension CommomApi{
         switch self {
         case .register(_),.phoneCodeLogin(_),.emailCodeLogin(_),.login(_),.commitFeedback(_):
             return .post
-        case .updatePwd(_),.updateUserInfo(_),.updatePhone(_),.updateEmail(_):
+        case .updatePwd(_),.updateUserInfo(_),.updatePhone(_),.updateEmail(_),.bindDevice(_):
             return .put
+        case .deleteLoginRecord(_):
+            return .delete
         default:
             return .get
         }
@@ -205,6 +230,9 @@ extension CommomApi{
         case let .commitFeedback(para):
             params += para
             break
+        case let .bindDevice(para):
+            params += para
+            break
         default:
             break
         }
@@ -244,6 +272,11 @@ class HttpClient{
                 handleResponse(response, completionHandler: completionHandler)
             }
         }
+        else if target.method == .delete{
+            HTTP.DELETE(url,parameters: params,requestSerializer: JSONParameterSerializer()){ response in
+                handleResponse(response, completionHandler: completionHandler)
+            }
+        }
     }
     
     static func uploadImage<T: HandyJSON>(_ target: CommomApi,completionHandler:@escaping ((T, String) -> Void)) {
@@ -356,6 +389,11 @@ extension HttpClient{
                 return
             }
             completionHandler(T(),"请求失败")
+            if let code = dict["code"] as? Int,code == 50001{
+                YLprogressHUD.showAutoHiden(text: "当前账号已经退出登录"){
+                    CacheManager.shared.logout()
+                }
+            }
         }
     }
     
diff --git a/ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Contents.json b/ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Contents.json
new file mode 100644 (file)
index 0000000..391af57
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "Slice 7@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "Slice 7@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Slice 7@2x.png b/ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Slice 7@2x.png
new file mode 100644 (file)
index 0000000..f59b190
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Slice 7@2x.png differ
diff --git a/ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Slice 7@3x.png b/ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Slice 7@3x.png
new file mode 100644 (file)
index 0000000..e5acb6c
Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Slice 7@3x.png differ
index c5ee6b0..f75f176 100644 (file)
@@ -8,7 +8,7 @@
 import UIKit
 
 class BaseTC:UITabBarController{
-    let homeVC = HomeVC()
+    let homeVC = HomeDeviceVC()
     let modeVC = ModeVC()
     let sleepVC = SleepVC()
     let customerVC = CustomerVC()
@@ -55,6 +55,6 @@ class BaseTC:UITabBarController{
         self.tabBar.backgroundColor = .white
         view.backgroundColor = .color(.bgColor)
 
-        self.selectedIndex = 3
+        self.selectedIndex = 0
     }
 }
index d6e510c..752957f 100644 (file)
@@ -64,11 +64,14 @@ class BaseCollectionViewCell: UICollectionViewCell {
     override init(frame: CGRect) {
         super.init(frame: frame)
         backgroundColor = .clear
+        setupViews()
+        setupLayout()
     }
     
     override func awakeFromNib() {
         super.awakeFromNib()
         backgroundColor = .clear
+        
     }
     
     required init?(coder: NSCoder) {
@@ -84,4 +87,10 @@ class BaseCollectionViewCell: UICollectionViewCell {
     static func cellForNib()-> UINib{
         return UINib.init(nibName: String(describing: Self.classForCoder()), bundle: nil)
     }
+    func setupViews(){
+        
+    }
+    func setupLayout(){
+        
+    }
 }
index b438f6d..df06f13 100644 (file)
@@ -27,3 +27,23 @@ class BaseTableViewSetionHeader: UITableViewHeaderFooterView {
         
     }
 }
+
+
+extension BaseColletionSetionHeader: YCColletionHeaderFooterInitProtocol { }
+class BaseColletionSetionHeader: UICollectionReusableView {
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        setupViews()
+        setupLayout()
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    func setupViews(){
+        
+    }
+    func setupLayout(){
+        
+    }
+}
index 814542b..1820b0c 100644 (file)
@@ -34,7 +34,7 @@ open class BaseVC:UIViewController {
         view.addSubview(navBar)
 //        self.automaticallyAdjustsScrollViewInsets = false
         navigationController?.navigationBar.isHidden = true
-        if self.isKind(of: HomeVC.self) ||
+        if self.isKind(of: HomeDeviceVC.self) ||
             self.isKind(of: ModeVC.self) ||
             self.isKind(of: SleepVC.self) ||
             self.isKind(of: CustomerVC.self) ||
index 4461200..3a0dd55 100644 (file)
@@ -48,21 +48,21 @@ class CustomerVC:BaseVC{
         scrollView.snp.makeConstraints { make in
             make.top.equalTo(kNavBtm)
             make.left.equalToSuperview()
-            make.height.equalTo(kScreenHei - kNavBtm - 56)
+            make.height.equalTo(kScreenHei - kNavBtm - kTabbarHei)
             make.width.equalTo(kScreenWid)
         }
         tableView.snp.makeConstraints { make in
             make.top.equalToSuperview()
             make.left.equalToSuperview()
             make.width.equalTo(kScreenWid)
-            make.height.equalTo(kScreenHei - kNavBtm - 56 - 62)
+            make.height.equalTo(kScreenHei - kNavBtm - kTabbarHei - 72)
         }
         
         toolBar.snp.makeConstraints { make in
             make.width.centerX.equalToSuperview()
             make.height.equalTo(52)
             make.top.equalTo(tableView.snp.bottom).offset(10)
-            make.bottom.equalToSuperview()
+            make.bottom.equalToSuperview().inset(-72)
         }
     }
     override func setupData() {
diff --git a/ShenYin/ShenYin/UI/Home/Device/DeviceModel.swift b/ShenYin/ShenYin/UI/Home/Device/DeviceModel.swift
new file mode 100644 (file)
index 0000000..3d733dd
--- /dev/null
@@ -0,0 +1,21 @@
+//
+//  DeviceModel.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/20.
+//
+
+import HandyJSON
+
+class HomeDeviceModelRoot:ResponseBaseModel {
+    var data = [HomeDeviceModel]()
+}
+class HomeDeviceModel:HandyJSON {
+    var id = ""
+    var productId = ""
+    var deviceId = ""
+    var image = ""
+    var productName = ""
+    
+    required public init() { }
+}
diff --git a/ShenYin/ShenYin/UI/Home/Device/DeviceVM.swift b/ShenYin/ShenYin/UI/Home/Device/DeviceVM.swift
new file mode 100644 (file)
index 0000000..22f1046
--- /dev/null
@@ -0,0 +1,25 @@
+//
+//  DeviceVM.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/20.
+//
+
+
+
+
+class DeviceVM:BaseVM{
+    static let shared = DeviceVM()
+    var dataList = [HomeDeviceModel]()
+
+    public func refresh(completion:@escaping (_ result:HomeDeviceModelRoot) ->Void) {
+        let api = CommomApi.deviceList
+        HttpClient.request(api) {[weak self] (model:HomeDeviceModelRoot,res) in
+            guard let self = self else {return}
+            if model.isSuccess{
+                self.dataList = model.data
+            }
+            completion(model)
+        }
+    }
+}
diff --git a/ShenYin/ShenYin/UI/Home/Device/HomeDeviceCell.swift b/ShenYin/ShenYin/UI/Home/Device/HomeDeviceCell.swift
new file mode 100644 (file)
index 0000000..3b3423b
--- /dev/null
@@ -0,0 +1,74 @@
+//
+//  HomeDeviceCell.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/20.
+//
+
+import UIKit
+import Kingfisher
+class HomeDeviceCell: BaseCollectionViewCell {
+    let bgView = UIView()
+    let icon = UIImageView()
+    let titleLabel = UILabel()
+    let valueLabel = UILabel()
+    var model:HomeDeviceModel!{
+        didSet{
+            titleLabel.text = model.productName + model.deviceId.prefix(5)
+            valueLabel.text = "未连接"
+            icon.kf.setImage(with: model.image.yc.asUrl)
+        }
+    }
+    var enable:Bool = false{
+        didSet{
+            if enable {
+                alpha = 1
+            }
+            else{
+                alpha = 0.3
+            }
+        }
+    }
+    
+    override func setupViews() {
+        addSubview(bgView)
+        bgView.addSubviews([titleLabel,icon,valueLabel])
+        bgView.do { v in
+            v.backgroundColor = .white
+            v.yc.setCornerRadius(20)
+        }
+        icon.do { v in
+//            v.backgroundColor = .lightGray
+        }
+        titleLabel.do { (v) in
+            v.textColor = .rgb(0x313230)
+            v.font = .bold(14)
+            v.text = "Deep Lite2 Ultra"
+        }
+        valueLabel.do { (v) in
+            v.textColor = .rgb(0x8d8d8d)
+            v.font = .regular(12)
+            v.text = "未连接"
+        }
+    }
+    
+    override func setupLayout() {
+        bgView.snp.makeConstraints { make in
+            make.left.right.top.bottom.equalToSuperview()
+        }
+
+        titleLabel.snp.makeConstraints { (m) in
+            m.top.equalToSuperview().inset(15)
+            m.left.equalToSuperview().offset(15)
+        }
+        valueLabel.snp.makeConstraints { (m) in
+            m.top.equalTo(titleLabel.snp.bottom).offset(1)
+            m.left.equalTo(titleLabel)
+        }
+        icon.snp.makeConstraints { (m) in
+            m.size.equalTo(56)
+            m.left.equalToSuperview().inset(15)
+            m.top.equalToSuperview().inset(58)
+        }
+    }
+}
diff --git a/ShenYin/ShenYin/UI/Home/Device/HomeDeviceVC.swift b/ShenYin/ShenYin/UI/Home/Device/HomeDeviceVC.swift
new file mode 100644 (file)
index 0000000..565f8f0
--- /dev/null
@@ -0,0 +1,115 @@
+//
+//  HomeDeviceVC.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/20.
+//
+
+import UIKit
+
+class HomeDeviceVC:BaseVC{
+    private var collectionView:UICollectionView!
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+    }
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        reloadData()
+    }
+    
+    override func setupViews() {
+        navBar.leftView.isHidden = true
+        navBar.rightView.setImage(UIImage(named: "add_icon"), for: .normal)
+
+        
+        let layout = UICollectionViewFlowLayout()
+        //layout.minimumInteritemSpacing = 10.fitR
+        layout.minimumLineSpacing = 18
+        layout.itemSize = CGSize(width: (kScreenWid-54)/2, height: 128)
+        collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
+        view.addSubview(collectionView)
+        
+        collectionView.do { (v) in
+            v.dataSource = self
+            v.delegate = self
+            v.backgroundColor = .clear
+            v.bounces = true
+            v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
+            v.register(HomeDeviceCell.self, forCellWithReuseIdentifier: HomeDeviceCell.reuseID)
+            v.register(HomeHeadInfoView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier:HomeHeadInfoView.reuseID )
+        }
+    }
+    override func setupLayout() {
+        collectionView.snp.makeConstraints { (m) in
+            m.top.equalTo(kNavBtm)
+            m.left.right.bottom.equalToSuperview()
+        }
+    }
+     func reloadData() {
+        DeviceVM.shared.refresh {[weak self] result in
+            self?.collectionView.reloadData()
+        }
+        CacheManager.shared.tokenIsValid(){[weak self] in
+            self?.collectionView.reloadData()
+        }
+    }
+    override func navRightViewTapped() {
+        //        App.shared.goToPage(AddDeviceVC())
+        //        let v = BasePickerView()
+        //        v.setupData(["2","3","4","5","6","7","8"])
+        //        v.selectedIndex(4)
+        //        v.show(animate: true)
+        
+        App.shared.goToPage(AddDeviceVC())
+    }
+    
+    @objc func addDeviceAction(){
+        let vc = AddDeviceVC()
+        self.navigationController?.pushViewController(vc, animated: true)
+    }
+}
+
+extension HomeDeviceVC:UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout {
+    func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 1
+    }
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return DeviceVM.shared.dataList.count
+    }
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let cell = HomeDeviceCell.cell(withCollectionView: collectionView, indexPath: indexPath)
+        cell.model = DeviceVM.shared.dataList[indexPath.row]
+        return cell
+    }
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        let model = DeviceVM.shared.dataList[indexPath.row]
+        if BleManager.shared.isConected(deviceId: model.deviceId) {
+            App.shared.goToPage(HomeVC())
+            return
+        }
+        
+        ConnectPopView.shared.stateDidChange(.connecting)
+        BleManager.shared.connect(deviceId: model.deviceId) { result in
+            if result{
+                ConnectPopView.shared.stateDidChange(.connected) {
+                    App.shared.goToPage(HomeVC())
+                }
+            }
+            else{
+                ConnectPopView.shared.stateDidChange(.failure)
+            }
+        }
+        
+    }
+    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView{
+            let header = HomeHeadInfoView.header(with: collectionView, indexPath: indexPath)
+        header.updateUI()
+            return header
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
+        return CGSize(width: kScreenWid, height: 160)
+    }
+}
diff --git a/ShenYin/ShenYin/UI/Home/Device/HomeHeadInfoView.swift b/ShenYin/ShenYin/UI/Home/Device/HomeHeadInfoView.swift
new file mode 100644 (file)
index 0000000..7df04b0
--- /dev/null
@@ -0,0 +1,61 @@
+//
+//  Untitled.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/20.
+//
+
+import UIKit
+
+class HomeHeadInfoView: BaseColletionSetionHeader {
+    let titleLabel = UILabel()
+    let deviceLabel = UILabel()
+    let nameLabel = UILabel()
+    override func setupViews() {
+        addSubviews([titleLabel,deviceLabel,nameLabel])
+        
+        titleLabel.do { (v) in
+            v.textColor = .rgb(0x121212)
+            v.font = .regular(32)
+            v.textAlignment = .left
+            v.text = "Juliaxxxx"
+        }
+        deviceLabel.do { (v) in
+            v.textColor = .rgb(0x666666)
+            v.font = .regular(14)
+            v.textAlignment = .left
+            v.text = "共4个智能设备"
+        }
+        nameLabel.do { (v) in
+            v.textColor = .hexString("#121212")
+            v.font = .medium(22)
+            v.textAlignment = .left
+            v.text = "设备"
+        }
+    }
+    
+    override func setupLayout() {
+        
+        titleLabel.snp.makeConstraints { (m) in
+            m.top.equalToSuperview().inset(10)
+            m.left.equalToSuperview().inset(32)
+            m.right.equalToSuperview().offset(-20)
+        }
+        deviceLabel.snp.makeConstraints { (m) in
+            m.top.equalToSuperview().inset(50)
+            m.left.equalToSuperview().inset(32)
+            m.height.equalTo(30)
+            m.width.equalTo(120)
+        }
+        nameLabel.snp.makeConstraints { (m) in
+            m.top.equalTo(deviceLabel.snp.bottom).offset(40)
+            m.left.equalToSuperview().inset(32)
+            m.height.equalTo(30)
+            m.width.equalTo(120)
+        }
+    }
+    func updateUI(){
+        titleLabel.text = CacheManager.shared.user.info.userName
+        deviceLabel.text = "共\(DeviceVM.shared.dataList.count)个智能设备"
+    }
+}
index 49557e2..71e38ab 100644 (file)
@@ -78,7 +78,7 @@ class HomeFunctionView: BaseView {
         var y = 0.0
         for (index, value) in list.enumerated() {
             let view = HomeCell()
-            view.enable = App.shared.deviceValid
+            view.enable = BleManager.shared.isConnetd
             addSubview(view)
             
             view.type = value
@@ -99,7 +99,7 @@ class HomeFunctionView: BaseView {
     @objc func deviceChange(_ noti:Notification){
         for item in self.subviews{
             if let view = item as? HomeCell  {
-                view.enable = App.shared.deviceValid
+                view.enable = BleManager.shared.isConnetd
             }
         }
     }
@@ -170,7 +170,7 @@ class HomeCell: BaseView {
         }
     }
     @objc func tapAction(){
-        if !App.shared.deviceValid {
+        if !BleManager.shared.isConnetd {
             return
         }
         switch type {
index a940d84..d755af9 100644 (file)
@@ -50,7 +50,7 @@ class HomeHeadView: BaseView {
             v.font = .regular(14)
             v.text = "随时随地 一键开录"
         }
-        self.enable = App.shared.deviceValid
+        self.enable = BleManager.shared.isConnetd
         NotificationManager.add(self, selector: #selector(deviceChange(_:)), forName: .deviceValidChange)
     }
     
@@ -79,12 +79,12 @@ class HomeHeadView: BaseView {
         }
     }
     @objc func chatAction(){
-        if App.shared.deviceValid {
+        if BleManager.shared.isConnetd {
             App.shared.goToPage(ChatVC())
         }
     }
     @objc func deviceChange(_ noti:Notification){
-        self.enable = App.shared.deviceValid
+        self.enable = BleManager.shared.isConnetd
     }
 }
 
similarity index 70%
rename from ShenYin/ShenYin/UI/Scan/BleScanVC.swift
rename to ShenYin/ShenYin/UI/Home/Scan/BleScanVC.swift
index aa11e6a..0637fab 100644 (file)
@@ -8,7 +8,7 @@
 import UIKit
 import CoreBluetooth
 import DGActivityIndicatorView
-
+import JoyGemAI
 enum BleScanState{
     case scan
     case has
@@ -18,7 +18,7 @@ enum BleScanState{
 class BleScanVC: BaseVC{
     let headerView = BleScanHeaderView(frame: CGRect(x: 0, y: 0, width: kScreenWid, height: 280))
     let tableView = UITableView(frame: .zero, style: .plain)
-    var list:[CBPeripheral] = []
+    var list:[DCPeripheral] = []
     
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -62,8 +62,94 @@ class BleScanVC: BaseVC{
     func startScan(){
         list = []
         tableView.reloadData()
-        BleManager.shared.startScanDevice()        
+        BleManager.shared.startScanDevice()
+        
+        BleManager.shared.didConnectDeviceCallback = { [weak self]  device in
+            self?.getDeviceInfo(device)
+        }
+        BleManager.shared.conetedFailureCallback = { [weak self]  device in
+            self?.showError()
+        }
+    }
+    
+    func startConnecte(_ data:DCPeripheral){
+        ConnectPopView.shared.stateDidChange(.connecting)
+        BleManager.shared.connectPeripheral(data.device)
+
+    }
+    /// 带冒号
+    func getDeviceInfo(_ device:CBPeripheral){
+        guard let prodName = device.name,prodName.count > 0 else{
+            showError()
+            BleManager.shared.cancel()
+            return
+        }
+        getMac {[weak self] mac in
+            guard let self = self else {return}
+            guard let mac = mac else{
+                BleManager.shared.cancel()
+                self.showError()
+                return
+            }
+            startBind(dn: "EEBD03250F66", mac: mac, prodName: prodName)
+        }
+    }
+    
+    func startBind(dn deviceId:String,mac:String,prodName:String){
+        var params:[String:Any] = [:]
+        params["deviceId"] = deviceId//dn
+        params["mac"] = mac
+        params["prodName"] =  prodName
+        let api = CommomApi.bindDevice(param: params)
+        HttpClient.request(api) {[weak self] (model:ResponseBaseModel,res) in
+            guard let self = self else {return}
+            if model.isSuccess{
+                ConnectPopView.shared.stateDidChange(.connected) {
+                    App.shared.didLogin()
+                }
+            }
+            else{
+                self.showError()
+                BleManager.shared.cancel()
+            }
+        }
+    }
+    func getMac(finished:@escaping ((_ mac:String?)->Void)) {
+        JoyGemAIManager.shared().device.macAddress { edr, ble in
+            Logger.debug("edr:\(edr),ble:\(ble)");
+            finished(ble)
+        } error: { error in
+            Logger.debug("\(error!.localizedDescription)")
+            finished(nil)
+        }
+    }
+    func fshowSuccess(){
+        
     }
+    func showError(){
+        ConnectPopView.shared.stateDidChange(.failure) {
+            
+        }
+    }
+    
+    func formatStringAsMacAddress(_ input: String) -> String? {
+        guard input.count == 12 else {
+            return nil
+        }
+        var result = ""
+        var startIndex = input.startIndex
+        while startIndex < input.endIndex {
+            let endIndex = input.index(startIndex, offsetBy: min(2, input.distance(from: startIndex, to: input.endIndex)))
+            let substring = String(input[startIndex..<endIndex])
+            result += substring
+            if endIndex < input.endIndex {
+                result += ":"
+            }
+            startIndex = endIndex
+        }
+        return result
+    }
+
 }
 
 extension BleScanVC:UITableViewDelegate,UITableViewDataSource {
@@ -78,7 +164,7 @@ extension BleScanVC:UITableViewDelegate,UITableViewDataSource {
     }
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         let data = list[indexPath.row]
-        BleManager.shared.connectPeripheral(data)
+        startConnecte(data)
     }
 }
 
@@ -177,10 +263,10 @@ class BleScanCell: BaseTableViewCell {
     let icon = UIImageView()
     let titleLabel = UILabel()
     
-    var model:CBPeripheral!{
+    var model:DCPeripheral!{
         didSet{
-            titleLabel.text = model.name
-//            titleLabel.text = titleLabel.text! + "\n" + model.identifier.uuidString
+            titleLabel.text = model.device.name
+            titleLabel.text = titleLabel.text! + "\n" + model.mac
             titleLabel.adjustsFontSizeToFitWidth = true
         }
     }
index 0b47d5f..325aeec 100644 (file)
@@ -35,8 +35,8 @@ class AppLaunch:NSObject{
             App.shared.goToLogin()
         }
         
-        let list = DeviceCache.shared.getDeviceList()
-        Logger.debug("缓存设备",list)
+//        let list = DeviceCache.shared.getDeviceList()
+//        Logger.debug("缓存设备",list)
 //        if list.count > 0 {
 //            CacheManager.shared.currentDevice = list.first
 //            BleManager.shared.startScanDevice()
index dfaa579..7e6888b 100644 (file)
@@ -70,11 +70,4 @@ extension App{
     }
 }
 
-extension App{
-    var deviceValid:Bool{
-//        return BleManager.shared.isConnetd
-        return !BleManager.shared.isConnetd
-    }
-}
-
 
index 2e6ef0e..992539a 100644 (file)
@@ -9,13 +9,24 @@ import UIKit
 import CoreBluetooth
 import JoyGemAI
 import AVFAudio
+
+class DCPeripheral{
+    var device:CBPeripheral!
+    var mac = ""
+    init(device: CBPeripheral!, mac: String = "") {
+        self.device = device
+        self.mac = mac
+    }
+}
+
 class BleManager: NSObject {
-    var discoverDeviceCallback: (([CBPeripheral]) -> Void)?
+    var discoverDeviceCallback: (([DCPeripheral]) -> Void)?
     var didConnectDeviceCallback: ((CBPeripheral) -> Void)?
     var disConnectDeviceCallback: ((CBPeripheral) -> Void)?
+    var conetedFailureCallback: ((CBPeripheral) -> Void)?
     
     private var centralManager: CBCentralManager!
-    var devices: [CBPeripheral] = []
+    var devices: [DCPeripheral] = []
     private var connectPeripheral: CBPeripheral?
     
     private override init() {
@@ -54,7 +65,7 @@ extension BleManager:CBCentralManagerDelegate{
     }
     
     func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
-        if devices.contains(peripheral) {
+        if devices.map({ $0.device }).contains(peripheral) {
             return
         }
         
@@ -64,11 +75,10 @@ extension BleManager:CBCentralManagerDelegate{
         if peripheral.identifier.uuidString != "DB209A55-DD0E-5031-29FA-40D734C34971" {
             return
         }
-        
-        devices.append(peripheral)
+        let mac = getMac(advertisementData) ?? ""
+        devices.append(DCPeripheral(device: peripheral,mac: mac))
         Logger.debug(peripheral)
         discoverDeviceCallback?(devices)
-        checkConnect()
     }
     
     func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
@@ -77,21 +87,40 @@ extension BleManager:CBCentralManagerDelegate{
         connectPeripheral = peripheral
         peripheral.delegate = self
         peripheral.discoverServices([CBUUID(string: kJoyGemAIServiceUUID)]);
+        self.isConnetd = true
         didConnectDeviceCallback?(connectPeripheral!)
+        NotificationManager.post(type: .deviceValidChange, objtect: nil)
     }
     func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
         Logger.debug("peripheral: %@ failed to connect", peripheral)
-        YLprogressHUD.hidden()
+        conetedFailureCallback?(peripheral)
     }
     func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
         Logger.debug("蓝牙已断开 \(peripheral)")
         self.isConnetd = false
         NotificationManager.post(type: .deviceValidChange, objtect: nil)
         disConnectDeviceCallback?(peripheral)
-        checkConnect()
+    }
+    func getMac(_ dict:[String:Any])->String?{
+        if let advMsd = dict[CBAdvertisementDataManufacturerDataKey] as? Data {
+            if (advMsd.count >= 13) {
+                let mac = advMsd[7...12];
+                return mac.map { String($0, radix: 16).padLeft(toLength: 2, withPad: "0") }.joined(separator: ":")
+            } else {
+                return nil
+            }
+        } else {
+            return nil
+        }
+    }
+}
+// 扩展 String 来添加 padLeft 方法
+extension String {
+    func padLeft(toLength: Int, withPad pad: String) -> String {
+        let paddingCount = max(0, toLength - self.count)
+        return String(repeating: pad, count: paddingCount) + self
     }
 }
-
 
 extension BleManager: CBPeripheralDelegate {
     
@@ -143,6 +172,7 @@ extension BleManager: CBPeripheralDelegate {
 }
 
 extension BleManager{
+    
     // MARK: - Public Methods
     func startScanDevice() {
         centralManager.stopScan()
@@ -154,24 +184,24 @@ extension BleManager{
     }
     
     func checkConnect(){
-        let list = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: kJoyGemAIServiceUUID)])
-        for item in list {
-            devices.append(item)
-            break
-        }
-
-        for item in devices{
-            if let current = CacheManager.shared.currentDevice,current.uuid == item.identifier.uuidString {
-                connectPeripheral(item)
-                return
-            }
-        }
-        
-        if connectPeripheral == nil || connectPeripheral?.state == .disconnected {
-            DispatchQueue.main.asyncAfter(deadline:.now() + 3) {
-                self.checkConnect()
-            }
-        }
+//        let list = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: kJoyGemAIServiceUUID)])
+//        for item in list {
+//            devices.append(item)
+//            break
+//        }
+//
+//        for item in devices{
+//            if let current = CacheManager.shared.currentDevice,current.uuid == item.identifier.uuidString {
+//                connectPeripheral(item)
+//                return
+//            }
+//        }
+//        
+//        if connectPeripheral == nil || connectPeripheral?.state == .disconnected {
+//            DispatchQueue.main.asyncAfter(deadline:.now() + 3) {
+//                self.checkConnect()
+//            }
+//        }
     }
     
     func stopScanDevice() {
@@ -196,12 +226,6 @@ extension BleManager : JoyGemAIManagerDelegate {
     }
     
     func joyGemAuthSuccessCallback() {
-        self.isConnetd = true
-        NotificationManager.post(type: .deviceValidChange, objtect: nil)
-        ConnectPopView().showAutoHide {
-//            App.shared.enter()
-        }
-        CacheManager.shared.saveDevice(DeviceModel(name: connectPeripheral?.name ?? "", uuid: connectPeripheral?.identifier.uuidString ?? ""))
         PcmSound.sharedInstance().setup(16000, withchannels: 1)
         let options: AVAudioSession.CategoryOptions = [.allowBluetooth]
         do {
@@ -237,3 +261,32 @@ extension BleManager : JoyGemAIManagerDataSource {
         }
     }
 }
+
+
+extension BleManager{
+    func isConected(deviceId:String)->Bool{
+        return false
+    }
+    func connect(deviceId:String, finished:@escaping ((Bool) -> Void)){
+        BleManager.shared.cancel()
+        BleManager.shared.discoverDeviceCallback = { list in
+            if list.count <= 0 {
+                return
+            }
+            
+//            for item in list {
+//                if item.name == "" {
+//                    
+//                }
+//            }
+            BleManager.shared.connectPeripheral(list[0].device)
+        }
+        BleManager.shared.didConnectDeviceCallback = { device in
+            finished(true)
+        }
+        BleManager.shared.conetedFailureCallback = {  device in
+            finished(false)
+        }
+        BleManager.shared.startScanDevice()
+    }
+}
index 1a30692..a3aef36 100644 (file)
@@ -9,7 +9,7 @@ import UIKit
 class AccountSetVC: BaseVC{
     let header = AccountSetHeaderView(frame: CGRect(x: 0, y: 0, width: kScreenWid, height: 50))
     let tableView = UITableView(frame: .zero, style: .plain)
-    let list:[(title:String,list:[CustomCellType])] = [("登录方式",[.account,.phone,.email,.thirdLogin]),]
+    let list:[(title:String,list:[CustomCellType])] = [("登录方式",[.account,.phone,.email,.thirdLogin]),("基本资料",[.loginManager]),]
     
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -82,8 +82,8 @@ extension AccountSetVC:UITableViewDelegate,UITableViewDataSource {
             App.shared.goToPage(SetPhoneVC())
         case .email:
             App.shared.goToPage(SetEmailVC())
-        case .thirdLogin:
-//            App.shared.goToPage(SetAccountVC())
+        case .loginManager:
+            App.shared.goToPage(DeviceLoginVC())
             break
         default:
             break
diff --git a/ShenYin/ShenYin/UI/Mine/Device/DeviceInfoVC.swift b/ShenYin/ShenYin/UI/Mine/Device/DeviceInfoVC.swift
new file mode 100644 (file)
index 0000000..5d075de
--- /dev/null
@@ -0,0 +1,137 @@
+//
+//  DeviceInfoVC.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/21.
+//
+
+import UIKit
+class DeviceInfoVC: BaseVC{
+    let tableView = UITableView(frame: .zero, style: .plain)
+    var vm = MyDeviceInfoVM()
+    
+    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 = .color(.bgColor)
+            v.delegate = self
+            v.dataSource = self
+            v.rowHeight = 52
+            v.sectionHeaderHeight = 52
+            v.separatorStyle = .none
+            v.register(MyDeviceInfoCell.self, forCellReuseIdentifier: MyDeviceInfoCell.reuseID)
+            v.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: kScreenHei, height: 20))
+            v.bounces = false
+        }
+
+        
+        view.addSubviews([tableView])
+        
+        
+    }
+    override func setupLayout() {
+        tableView.snp.makeConstraints { make in
+            make.left.right.equalToSuperview()
+            make.top.equalToSuperview().inset(kNavBtm)
+            make.bottom.equalToSuperview()
+        }
+    }
+}
+
+extension DeviceInfoVC:UITableViewDelegate,UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return vm.list.count
+    }
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = MyDeviceInfoCell.cell(withTableView: tableView, indexPath: indexPath)
+        let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
+        cell.model = vm.list[indexPath.row]
+        if indexPath.row == 0 {
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 12, height: 12))
+        }
+        else if indexPath.row == 4{
+            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))
+        }
+        return cell
+    }
+
+}
+
+
+class MyDeviceListCellModel {
+    var title = ""
+    var value = ""
+    init(title: String = "", value: String = "") {
+        self.title = title
+        self.value = value
+    }
+}
+fileprivate class MyDeviceInfoCell: BaseTableViewCell {
+    let bgView = UIView()
+    let titleLabel = UILabel()
+    let valueLabel = UILabel()
+ 
+    var model:MyDeviceListCellModel!{
+        didSet{
+            titleLabel.text = model.title
+            valueLabel.text = model.value
+        }
+    }
+    
+    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.addSubview(bgView)
+        bgView.addSubviews([titleLabel,valueLabel])
+        
+        backgroundColor = .rgb(0xF7F7F7)
+        contentView.backgroundColor = .clear
+        selectionStyle = .none
+        bgView.do { v in
+            v.backgroundColor = UIColor.white
+        }
+        titleLabel.do { (v) in
+            v.textColor = .rgb(0x666666)
+            v.font = .bold(14)
+            v.textAlignment = .left
+        }
+        valueLabel.do { v in
+            v.textColor = .rgb(0x121212)
+            v.font = .regular(14)
+            v.textAlignment = .right
+        }
+    }
+    
+    override func setupLayout() {
+        bgView.snp.makeConstraints { (m) in
+            m.left.right.equalToSuperview().inset(18)
+            m.top.equalToSuperview()
+            m.bottom.equalToSuperview()
+        }
+        titleLabel.snp.makeConstraints { (m) in
+            m.centerY.equalToSuperview()
+            m.left.equalToSuperview().offset(16)
+            m.height.equalToSuperview()
+        }
+        valueLabel.snp.makeConstraints { (m) in
+            m.centerY.equalToSuperview()
+            m.right.equalToSuperview().inset(16)
+            m.height.equalToSuperview()
+            m.left.equalToSuperview().inset(90)
+        }
+    }
+    
+    
+}
diff --git a/ShenYin/ShenYin/UI/Mine/Device/MyDeviceInfoVM.swift b/ShenYin/ShenYin/UI/Mine/Device/MyDeviceInfoVM.swift
new file mode 100644 (file)
index 0000000..4db7032
--- /dev/null
@@ -0,0 +1,38 @@
+//
+//  MyDeviceVM.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/21.
+//
+
+class MyDeviceInfo{
+    var deviceName = ""
+    var mac = ""
+    var heardWear = ""
+    var softWear = ""
+    var sapce = ""
+}
+
+
+
+class MyDeviceInfoVM:BaseVM{
+    var mode = MyDeviceInfo()
+    var list:[MyDeviceListCellModel] = [
+        MyDeviceListCellModel(title: "设备名称",value: "Deep Gen2 Max"),
+        MyDeviceListCellModel(title: "设备地址",value: "E7:FA:D8:C8:CC:62"),
+        MyDeviceListCellModel(title: "硬件版本",value: "SY_Gen2_Max_V1.0"),
+        MyDeviceListCellModel(title: "软件版本",value: "SY_Gen2_Max_V1.0.1"),
+        MyDeviceListCellModel(title: "剩余容量",value: "100MB"),
+    ]
+    
+//    public func refresh(completion:@escaping (_ result:HomeDeviceModelRoot) ->Void) {
+//        let api = CommomApi.deviceList
+//        HttpClient.request(api) {[weak self] (model:HomeDeviceModelRoot,res) in
+//            guard let self = self else {return}
+//            if model.isSuccess{
+//                self.dataList = model.data
+//            }
+//            completion(model)
+//        }
+//    }
+}
@@ -6,10 +6,9 @@
 //
 
 import UIKit
-class MyDeviceList: BaseVC{
+class MyDeviceListVC: BaseVC{
     let tableView = UITableView(frame: .zero, style: .plain)
     let bottomView = HistoryBottonView()
-    var list = [1,2,3,4,5,6,7,8]
     
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -37,21 +36,22 @@ class MyDeviceList: BaseVC{
             make.bottom.equalToSuperview()
         }
     }
+
 }
 
-extension MyDeviceList:UITableViewDelegate,UITableViewDataSource {
+extension MyDeviceListVC:UITableViewDelegate,UITableViewDataSource {
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return list.count
+        return DeviceVM.shared.dataList.count
     }
     
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = MyDeviceListCell.cell(withTableView: tableView, indexPath: indexPath)
-        //        cell.type = list[indexPath.row]
+        cell.model = DeviceVM.shared.dataList[indexPath.row]
         return cell
     }
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        let data = list[indexPath.row]
-        
+        let data = DeviceVM.shared.dataList[indexPath.row]
+        App.shared.goToPage(DeviceInfoVC())
     }
 //    // 实现滑动删除功能
 //    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
@@ -68,10 +68,20 @@ extension MyDeviceList:UITableViewDelegate,UITableViewDataSource {
 //        return true
 //    }
 }
+
+
+
+
 class MyDeviceListCell: BaseTableViewCell {
     let bgView = UIView()
-    let playIcon = UIImageView()
+    let icon = UIImageView()
     let titleLabel = UILabel()
+    let nextIcon = UIImageView()
+    var model:HomeDeviceModel!{
+        didSet{
+            titleLabel.text = model.productName + model.deviceId.prefix(5)
+        }
+    }
 
     override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
         super.init(style: style, reuseIdentifier: reuseIdentifier)
@@ -82,7 +92,7 @@ class MyDeviceListCell: BaseTableViewCell {
     }
     override func setupViews() {
         contentView.addSubview(bgView)
-        bgView.addSubviews([playIcon,titleLabel])
+        bgView.addSubviews([icon,titleLabel,nextIcon])
         
         backgroundColor = .rgb(0xF7F7F7)
         contentView.backgroundColor = .clear
@@ -99,9 +109,10 @@ class MyDeviceListCell: BaseTableViewCell {
             v.text = "Deep Gen2 Max"
         }
         
-        playIcon.do { v in
+        icon.do { v in
             v.image = UIImage(named: "device_list_icon")
         }
+        nextIcon.image = UIImage(named: "arrow_next")
     }
     
     override func setupLayout() {
@@ -112,7 +123,7 @@ class MyDeviceListCell: BaseTableViewCell {
             m.height.equalTo(74)
         }
         
-        playIcon.snp.makeConstraints { (m) in
+        icon.snp.makeConstraints { (m) in
             m.size.equalTo(24)
             m.centerY.equalToSuperview()
             m.left.equalToSuperview().inset(16)
@@ -121,6 +132,11 @@ class MyDeviceListCell: BaseTableViewCell {
             m.centerY.equalToSuperview()
             m.left.equalToSuperview().offset(56)
         }
-       
+        nextIcon.snp.makeConstraints { (m) in
+            m.width.equalTo(8)
+            m.height.equalTo(12)
+            m.right.equalToSuperview().inset(15)
+            m.centerY.equalToSuperview()
+        }
     }
 }
diff --git a/ShenYin/ShenYin/UI/Mine/DeviceInfoVC.swift b/ShenYin/ShenYin/UI/Mine/DeviceInfoVC.swift
deleted file mode 100644 (file)
index 74358ee..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-//
-//  DeviceInfoVC.swift
-//  ShenYin
-//
-//  Created by liangshi on 2025/8/6.
-//
-
-import UIKit
-
-class DeviceInfoVC:BaseVC{
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        
-    }
-    override func setupViews() {
-        self.navBar.title = "设备详情"
-    }
-    override func setupLayout() {
-        
-    }
-    
-}
index bc73177..48c92de 100644 (file)
@@ -134,7 +134,7 @@ class FeedbackCommitVC:BaseVC{
         params["type"] = "report"
         params["fileUrl"] = url
         let api =  CommomApi.uploadImage(param: params)
-        HttpClient.uploadImage(api){[weak self] (model:CommonDataModel, res) in
+        HttpClient.uploadImage(api){(model:CommonDataModel, res) in
             if model.isSuccess{
                 finished(model.data)
             }
@@ -147,7 +147,6 @@ class FeedbackCommitVC:BaseVC{
         let phone = contactView.textField.text ?? ""
         let content = contentView.textView.textView.text ?? ""
         
-        
         YLprogressHUD.showLoading()
         var params:[String:Any] = [:]
         params["type"] = typeView.type ?? ""
@@ -156,10 +155,11 @@ class FeedbackCommitVC:BaseVC{
         params["phone"] = phone
         
         let api =  CommomApi.commitFeedback(param: params)
-        HttpClient.request(api) {[weak self] (model:ResponseBaseModel,res) in
-            guard let self = self else {return}
+        HttpClient.request(api) { (model:ResponseBaseModel,res) in
             if model.isSuccess{
-                YLprogressHUD.hidden()
+                YLprogressHUD.showSuccess {
+                    App.shared.popPage()
+                }
             }
             else{
                 YLprogressHUD.showAutoHiden(model.msg)
diff --git a/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginDetailVC.swift b/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginDetailVC.swift
new file mode 100644 (file)
index 0000000..9f64b66
--- /dev/null
@@ -0,0 +1,121 @@
+//
+//  DeviceLoginDetailVC.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/21.
+//
+
+import UIKit
+import MJRefresh
+class DeviceLoginDetailVC: BaseVC{
+    private let footerView = FooterView(frame: CGRect(x: 0, y: 0, width: kScreenWid, height: 200))
+    let tableView = UITableView(frame: .zero, style: .plain)
+    let list:[CommonCellModel]
+    let model:DeviceLoginRecordModel
+    
+    init(record :DeviceLoginRecordModel){
+        model = record
+        list = [
+            CommonCellModel(title: "设备名称", value: model.deviceName),
+            CommonCellModel(title: "设备ID", value: model.deviceId),
+            CommonCellModel(title: "最近登录时间", value: model.updateTime),
+        ]
+        super.init()
+    }
+    
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        // Do any additional setup after loading the view.
+    }
+    override func setupViews() {
+        navBar.do { v in
+            v.title = "登录设备管理"
+        }
+        footerView.model = model
+        
+        tableView.do { v in
+            v.backgroundColor = .rgb(0xF7F7F7)
+            v.delegate = self
+            v.dataSource = self
+            v.rowHeight = 52
+            v.separatorStyle = .none
+            v.register(CommonCell.self, forCellReuseIdentifier: CommonCell.reuseID)
+            v.tableFooterView = footerView
+        }
+        view.addSubviews([tableView])
+    }
+    override func setupLayout() {
+        tableView.snp.makeConstraints { make in
+            make.left.right.equalToSuperview()
+            make.top.equalTo(kNavBtm)
+            make.bottom.equalToSuperview()
+        }
+    }
+}
+
+extension DeviceLoginDetailVC:UITableViewDelegate,UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return list.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = CommonCell.cell(withTableView: tableView, indexPath: indexPath)
+        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 == 4{
+            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))
+        }
+        let model = list[indexPath.row]
+        cell.model = model
+        return cell
+    }
+}
+
+fileprivate class FooterView: BaseView {
+    let btn = CustomButton()
+    let label = UILabel()
+    var model:DeviceLoginRecordModel?
+    override func setupViews() {
+        addSubviews([btn,label])
+        
+        btn.set(title: "移除设备", style: .theme4,target: self, action: #selector(btnAction))
+        label.do { v in
+            v.text = "移除设备后会同步退出账号,在此设备上将无法访问该账号的数据"
+            v.textAlignment = .left
+            v.font = .regular(12)
+            v.textColor = .hexString("#999999")
+            v.numberOfLines = 0
+        }
+    }
+    override func setupLayout() {
+        btn.snp.makeConstraints { make in
+            make.left.right.equalToSuperview().inset(52)
+            make.height.equalTo(46)
+            make.top.equalToSuperview().inset(40)
+        }
+        label.snp.makeConstraints { make in
+            make.left.right.equalToSuperview().inset(52)
+            make.top.equalTo(btn.snp.bottom).offset(14)
+        }
+    }
+    @objc func btnAction(){
+        YLprogressHUD.showLoading()
+        let api = CommomApi.deleteLoginRecord(recordId: model!.id)
+        HttpClient.request(api) { (model:ResponseBaseModel,res) in
+            if model.isSuccess{
+                YLprogressHUD.showSuccess {
+                    App.shared.popPage()
+                }
+            }
+            else{
+                YLprogressHUD.showAutoHiden(model.msg)
+            }
+        }
+    }
+}
diff --git a/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginRecordModel.swift b/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginRecordModel.swift
new file mode 100644 (file)
index 0000000..848ac51
--- /dev/null
@@ -0,0 +1,33 @@
+//
+//  DeviceLoginRecordModel.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/21.
+//
+
+import HandyJSON
+
+class DeviceLoginRecordRoot:ResponseBaseModel {
+    var data = DeviceLoginRecordModelData()
+}
+class DeviceLoginRecordModelData:HandyJSON {
+    var list = [DeviceLoginRecordModel]()
+    var total = 0
+    var pageSize = 0
+    var currentPage = 0
+    var totalPage = 0
+    var hasNext = false
+    var hasPre = false
+    
+    required public init() { }
+}
+class DeviceLoginRecordModel:HandyJSON {
+    var id = ""
+    var userId = ""
+    var deviceName = ""
+    var deviceId = ""
+    var ip = ""
+    var createTime = ""
+    var updateTime = ""
+    required public init() { }
+}
diff --git a/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginVC.swift b/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginVC.swift
new file mode 100644 (file)
index 0000000..57d5518
--- /dev/null
@@ -0,0 +1,92 @@
+//
+//  Untitled.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/21.
+//
+
+import UIKit
+import MJRefresh
+class DeviceLoginVC: BaseVC{
+    let tableView = UITableView(frame: .zero, style: .plain)
+    let vm = DeviceLoginVM()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        // Do any additional setup after loading the view.
+    }
+    override func setupViews() {
+        navBar.do { v in
+            v.title = "登录设备管理"
+        }
+        
+        tableView.do { v in
+            v.backgroundColor = .rgb(0xF7F7F7)
+            v.delegate = self
+            v.dataSource = self
+            v.estimatedRowHeight = 84
+            v.separatorStyle = .none
+            v.register(MyDeviceListCell.self, forCellReuseIdentifier: MyDeviceListCell.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 viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        refreshAction()
+    }
+    @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 DeviceLoginVC:UITableViewDelegate,UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return vm.list.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = MyDeviceListCell.cell(withTableView: tableView, indexPath: indexPath)
+        let model = vm.list[indexPath.row]
+        cell.titleLabel.text = model.deviceName
+        cell.icon.image = UIImage(named: "device_record")
+        return cell
+    }
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let data = vm.list[indexPath.row]
+        App.shared.goToPage(DeviceLoginDetailVC(record: data))
+    }
+}
+
+
+
+
diff --git a/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginVM.swift b/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginVM.swift
new file mode 100644 (file)
index 0000000..3001ffd
--- /dev/null
@@ -0,0 +1,46 @@
+//
+//  deviceLoginManager.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/21.
+//
+
+class DeviceLoginVM:BaseVM{
+    var list = [DeviceLoginRecordModel]()
+    var page = 0
+
+    public func refresh(completion:@escaping (_ result:DeviceLoginRecordRoot) ->Void) {
+        self.page = 1
+        let api = CommomApi.loginRecord(page: page)
+        HttpClient.request(api) {[weak self] (model:DeviceLoginRecordRoot,res) in
+            guard let self = self else {return}
+            if model.isSuccess{
+                page = model.data.currentPage
+                if model.data.currentPage == 1 {
+                    list = model.data.list
+                }
+                else{
+                    list += model.data.list
+                }
+            }
+            completion(model)
+        }
+    }
+    public func loadMore(completion:@escaping (_ result:DeviceLoginRecordRoot) ->Void) {
+        self.page += 1
+        let api = CommomApi.loginRecord(page: page)
+        HttpClient.request(api) {[weak self] (model:DeviceLoginRecordRoot,res) in
+            guard let self = self else {return}
+            if model.isSuccess{
+                page = model.data.currentPage
+                if model.data.currentPage == 1 {
+                    list = model.data.list
+                }
+                else{
+                    list += model.data.list
+                }
+            }
+            completion(model)
+        }
+    }
+}
index 46918f9..32c4745 100644 (file)
@@ -62,7 +62,6 @@ class MineUserInfoView: BaseView {
             v.textColor = .rgb(0x666666)
             v.font = .regular(14)
             v.textAlignment = .center
-            v.text = "共4个智能设备"
             v.yc.setCornerRadius(15)
             v.yc.addTap(target: self, action:#selector(deviceListAction))
         }
@@ -94,6 +93,7 @@ class MineUserInfoView: BaseView {
             guard let self = self else {return}
             titleLabel.text = CacheManager.shared.user.info.userName
             icon.kf.setImage(with:CacheManager.shared.user.info.avatar.yc.asUrl )
+            deviceLabel.text = "共\(DeviceVM.shared.dataList.count)个智能设备"
         }
 
 
@@ -103,6 +103,6 @@ class MineUserInfoView: BaseView {
         App.shared.goToPage(AccountSetVC())
     }
     @objc func deviceListAction(){
-        App.shared.goToPage(MyDeviceList())
+        App.shared.goToPage(MyDeviceListVC())
     }
 }
index 0357c07..070b62c 100644 (file)
@@ -40,6 +40,9 @@ class MineVC: BaseVC{
         
         CacheManager.shared.user.getStorageSize { result in
             
+        }
+        DeviceVM.shared.refresh { result in
+            
         }
     }
     override func viewDidAppear(_ animated: Bool) {
index a5f0957..36c8a8d 100644 (file)
@@ -15,7 +15,7 @@ public enum CustomCellType: Int {
     case phone
     case email
     case thirdLogin
-    
+    case loginManager
     
     case update
     case userAgreement
@@ -45,6 +45,8 @@ public enum CustomCellType: Int {
             return "安全邮箱"
         case .thirdLogin:
             return "第三方账号"
+        case .loginManager:
+            return "登录设备管理"
             
         case .update:
             return "检查更新"
index 9a8abb5..82d2d3c 100644 (file)
@@ -77,6 +77,7 @@ class AudioPlayerVC:BaseVC{
             v.setImage(UIImage(named: "music_play"), for: .normal)
             v.setImage(UIImage(named: "music_pause"), for: .selected)
             v.addTarget(self, action: #selector(btnAction), for: .touchUpInside)
+            v.isEnabled = false
         }
         AudioPlayerManager.shared.progressCallback = {[weak self] current,duration in
             guard let self = self else {return}
@@ -137,10 +138,10 @@ class AudioPlayerVC:BaseVC{
     @objc func btnAction(){
         playBtn.isSelected = !playBtn.isSelected
         if playBtn.isSelected {
-            AudioPlayerManager.shared.pause()
+            AudioPlayerManager.shared.start()
         }
         else{
-            AudioPlayerManager.shared.start()
+            AudioPlayerManager.shared.pause()
         }
     }
     @objc func sliderChanged(_ slider:UISlider){
@@ -162,8 +163,10 @@ class AudioPlayerVC:BaseVC{
         }
     }
     func startPlay(path:String){
+        playBtn.isEnabled = true
         guard let path = Bundle.main.path(forResource: "大约在冬季", ofType: "mp3") else {return}
         AudioPlayerManager.shared.play(filePath: path)
         AudioPlayerManager.shared.start()
+        playBtn.isSelected = true
     }
 }
index 373b507..9a0b2c2 100644 (file)
@@ -8,12 +8,12 @@ import CoreBluetooth
 
 class CacheManager{
     static let shared = CacheManager()
-    var currentDevice:DeviceModel?
-    
-    func saveDevice(_ device: DeviceModel) {
-        self.currentDevice = device
-        DeviceCache.shared.saveDevice(device)
-    }
+//    var currentDevice:DeviceModel?
+//    
+//    func saveDevice(_ device: DeviceModel) {
+//        self.currentDevice = device
+//        DeviceCache.shared.saveDevice(device)
+//    }
     
     var user = UserModel()
     
diff --git a/ShenYin/ShenYin/UI/common/CommonCell.swift b/ShenYin/ShenYin/UI/common/CommonCell.swift
new file mode 100644 (file)
index 0000000..69c2455
--- /dev/null
@@ -0,0 +1,78 @@
+//
+//  CommonCell.swift
+//  ShenYin
+//
+//  Created by liangshi on 2025/10/21.
+//
+import UIKit
+
+class CommonCellModel {
+    var title = ""
+    var value = ""
+    init(title: String = "", value: String = "") {
+        self.title = title
+        self.value = value
+    }
+}
+class CommonCell: BaseTableViewCell {
+    let bgView = UIView()
+    let titleLabel = UILabel()
+    let valueLabel = UILabel()
+    
+    var model:CommonCellModel!{
+        didSet{
+            titleLabel.text = model.title
+            valueLabel.text = model.value
+        }
+    }
+    
+    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.addSubview(bgView)
+        bgView.addSubviews([titleLabel,valueLabel])
+        
+        backgroundColor = .rgb(0xF7F7F7)
+        contentView.backgroundColor = .clear
+        selectionStyle = .none
+        bgView.do { v in
+            v.backgroundColor = UIColor.white
+        }
+        titleLabel.do { (v) in
+            v.textColor = .rgb(0x666666)
+            v.font = .bold(14)
+            v.textAlignment = .left
+        }
+        valueLabel.do { v in
+            v.textColor = .rgb(0x121212)
+            v.font = .regular(14)
+            v.textAlignment = .right
+        }
+    }
+    
+    override func setupLayout() {
+        bgView.snp.makeConstraints { (m) in
+            m.left.right.equalToSuperview().inset(18)
+            m.top.equalToSuperview()
+            m.bottom.equalToSuperview()
+        }
+        titleLabel.snp.makeConstraints { (m) in
+            m.centerY.equalToSuperview()
+            m.left.equalToSuperview().offset(16)
+            m.height.equalToSuperview()
+        }
+        valueLabel.snp.makeConstraints { (m) in
+            m.centerY.equalToSuperview()
+            m.right.equalToSuperview().inset(16)
+            m.height.equalToSuperview()
+            m.left.equalToSuperview().inset(90)
+        }
+    }
+    
+    
+}
index a9ffd99..1f7834e 100644 (file)
@@ -11,6 +11,7 @@ enum CustomButtonStyle:Int{
     case theme1
     case theme2
     case theme3
+    case theme4
     case custom
 }
 
@@ -26,6 +27,11 @@ class CustomButton: UIButton {
             setTitleColor(.white, for: .normal)
             backgroundColor =  UIColor.color(.theme)
         }
+        else if style == .theme4 {
+            setTitleColor(.hexString("#FF4E4E"), for: .normal)
+            backgroundColor =  UIColor.hexString("#FF4E4E").withAlphaComponent(0.1)
+            titleLabel?.font = .bold(14)
+        }
         else if style == .theme3 {
             setTitleColor(.hexString("#121212"), for: .normal)
             backgroundColor =  UIColor.white
@@ -38,4 +44,5 @@ class CustomButton: UIButton {
             layer.borderColor = UIColor.color(.theme).cgColor
         }
     }
+    
 }
index 82879e3..b93ec1e 100644 (file)
@@ -7,7 +7,25 @@
 
 import UIKit
 
+enum ConnectPopState {
+    case connecting
+    case connected
+    case failure
+    
+    var text:String{
+        switch self {
+        case .connecting:
+            return "连接中"
+        case .connected:
+            return "连接成功"
+        case .failure:
+            return "连接失败"
+        }
+    }
+}
+
 class ConnectPopView: BasePresentView2 {
+    static let shared = ConnectPopView()
     let contentView = UIView()
     private let titleLabel = UILabel()
     private let imageView = UIImageView()
@@ -75,12 +93,25 @@ class ConnectPopView: BasePresentView2 {
         }
     }
     
-    func showAutoHide(finished: (() -> Void)? = nil){
-        show()
-        DispatchQueue.main.asyncAfter(deadline:.now() + 0.5) {
-            self.hide()
-            finished?()
+    func stateDidChange(_ state:ConnectPopState,finished: (() -> Void)? = nil){
+        desLabel.text = state.text
+        if state == .connecting {
+            show()
+        }
+        else{
+            DispatchQueue.main.asyncAfter(deadline:.now() + 0.5) {
+                self.hide()
+                finished?()
+            }
         }
     }
+
+//    func showAutoHide(finished: (() -> Void)? = nil){
+//        show()
+//        DispatchQueue.main.asyncAfter(deadline:.now() + 0.5) {
+//            self.hide()
+//            finished?()
+//        }
+//    }
 }