From: liangzhenying <867174124@qq.com> Date: Tue, 21 Oct 2025 10:10:38 +0000 (+0800) Subject: 完善设备绑定 X-Git-Tag: 1.0.2~86 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=a4d7fd1450d4a2e6f373d05500834513c327c51f;p=mirrors%2FShenYin.git 完善设备绑定 --- diff --git a/ShenYin/ShenYin/Api/HttpClient2.swift b/ShenYin/ShenYin/Api/HttpClient2.swift index 4bf340c..d353e49 100644 --- a/ShenYin/ShenYin/Api/HttpClient2.swift +++ b/ShenYin/ShenYin/Api/HttpClient2.swift @@ -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(_ 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 index 0000000..391af57 --- /dev/null +++ b/ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Contents.json @@ -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 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 index 0000000..e5acb6c Binary files /dev/null and b/ShenYin/ShenYin/Assets.xcassets/mine/device_record.imageset/Slice 7@3x.png differ diff --git a/ShenYin/ShenYin/UI/Base/BaseTC.swift b/ShenYin/ShenYin/UI/Base/BaseTC.swift index c5ee6b0..f75f176 100644 --- a/ShenYin/ShenYin/UI/Base/BaseTC.swift +++ b/ShenYin/ShenYin/UI/Base/BaseTC.swift @@ -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 } } diff --git a/ShenYin/ShenYin/UI/Base/BaseTableViewCell.swift b/ShenYin/ShenYin/UI/Base/BaseTableViewCell.swift index d6e510c..752957f 100644 --- a/ShenYin/ShenYin/UI/Base/BaseTableViewCell.swift +++ b/ShenYin/ShenYin/UI/Base/BaseTableViewCell.swift @@ -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(){ + + } } diff --git a/ShenYin/ShenYin/UI/Base/BaseTableViewSetionHeader.swift b/ShenYin/ShenYin/UI/Base/BaseTableViewSetionHeader.swift index b438f6d..df06f13 100644 --- a/ShenYin/ShenYin/UI/Base/BaseTableViewSetionHeader.swift +++ b/ShenYin/ShenYin/UI/Base/BaseTableViewSetionHeader.swift @@ -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(){ + + } +} diff --git a/ShenYin/ShenYin/UI/Base/BaseVC.swift b/ShenYin/ShenYin/UI/Base/BaseVC.swift index 814542b..1820b0c 100644 --- a/ShenYin/ShenYin/UI/Base/BaseVC.swift +++ b/ShenYin/ShenYin/UI/Base/BaseVC.swift @@ -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) || diff --git a/ShenYin/ShenYin/UI/Customer/CustomerVC.swift b/ShenYin/ShenYin/UI/Customer/CustomerVC.swift index 4461200..3a0dd55 100644 --- a/ShenYin/ShenYin/UI/Customer/CustomerVC.swift +++ b/ShenYin/ShenYin/UI/Customer/CustomerVC.swift @@ -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 index 0000000..3d733dd --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/Device/DeviceModel.swift @@ -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 index 0000000..22f1046 --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/Device/DeviceVM.swift @@ -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 index 0000000..3b3423b --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/Device/HomeDeviceCell.swift @@ -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 index 0000000..565f8f0 --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/Device/HomeDeviceVC.swift @@ -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 index 0000000..7df04b0 --- /dev/null +++ b/ShenYin/ShenYin/UI/Home/Device/HomeHeadInfoView.swift @@ -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)个智能设备" + } +} diff --git a/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift b/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift index 49557e2..71e38ab 100644 --- a/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift +++ b/ShenYin/ShenYin/UI/Home/Root/HomeFunctionView.swift @@ -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 { diff --git a/ShenYin/ShenYin/UI/Home/Root/HomeHeadView.swift b/ShenYin/ShenYin/UI/Home/Root/HomeHeadView.swift index a940d84..d755af9 100644 --- a/ShenYin/ShenYin/UI/Home/Root/HomeHeadView.swift +++ b/ShenYin/ShenYin/UI/Home/Root/HomeHeadView.swift @@ -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 } } diff --git a/ShenYin/ShenYin/UI/Scan/AddDeviceVC.swift b/ShenYin/ShenYin/UI/Home/Scan/AddDeviceVC.swift similarity index 100% rename from ShenYin/ShenYin/UI/Scan/AddDeviceVC.swift rename to ShenYin/ShenYin/UI/Home/Scan/AddDeviceVC.swift diff --git a/ShenYin/ShenYin/UI/Scan/BleScanVC.swift b/ShenYin/ShenYin/UI/Home/Scan/BleScanVC.swift similarity index 70% rename from ShenYin/ShenYin/UI/Scan/BleScanVC.swift rename to ShenYin/ShenYin/UI/Home/Scan/BleScanVC.swift index aa11e6a..0637fab 100644 --- a/ShenYin/ShenYin/UI/Scan/BleScanVC.swift +++ b/ShenYin/ShenYin/UI/Home/Scan/BleScanVC.swift @@ -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.. 0 { // CacheManager.shared.currentDevice = list.first // BleManager.shared.startScanDevice() diff --git a/ShenYin/ShenYin/UI/Launch/AppManager.swift b/ShenYin/ShenYin/UI/Launch/AppManager.swift index dfaa579..7e6888b 100644 --- a/ShenYin/ShenYin/UI/Launch/AppManager.swift +++ b/ShenYin/ShenYin/UI/Launch/AppManager.swift @@ -70,11 +70,4 @@ extension App{ } } -extension App{ - var deviceValid:Bool{ -// return BleManager.shared.isConnetd - return !BleManager.shared.isConnetd - } -} - diff --git a/ShenYin/ShenYin/UI/Launch/BleManager.swift b/ShenYin/ShenYin/UI/Launch/BleManager.swift index 2e6ef0e..992539a 100644 --- a/ShenYin/ShenYin/UI/Launch/BleManager.swift +++ b/ShenYin/ShenYin/UI/Launch/BleManager.swift @@ -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() + } +} diff --git a/ShenYin/ShenYin/UI/Mine/AccountSetVC.swift b/ShenYin/ShenYin/UI/Mine/AccountSetVC.swift index 1a30692..a3aef36 100644 --- a/ShenYin/ShenYin/UI/Mine/AccountSetVC.swift +++ b/ShenYin/ShenYin/UI/Mine/AccountSetVC.swift @@ -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 index 0000000..5d075de --- /dev/null +++ b/ShenYin/ShenYin/UI/Mine/Device/DeviceInfoVC.swift @@ -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 index 0000000..4db7032 --- /dev/null +++ b/ShenYin/ShenYin/UI/Mine/Device/MyDeviceInfoVM.swift @@ -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) +// } +// } +} diff --git a/ShenYin/ShenYin/UI/Mine/MyDeviceList.swift b/ShenYin/ShenYin/UI/Mine/Device/MyDeviceListVC.swift similarity index 79% rename from ShenYin/ShenYin/UI/Mine/MyDeviceList.swift rename to ShenYin/ShenYin/UI/Mine/Device/MyDeviceListVC.swift index 08c9eeb..18efb24 100644 --- a/ShenYin/ShenYin/UI/Mine/MyDeviceList.swift +++ b/ShenYin/ShenYin/UI/Mine/Device/MyDeviceListVC.swift @@ -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 index 74358ee..0000000 --- a/ShenYin/ShenYin/UI/Mine/DeviceInfoVC.swift +++ /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() { - - } - -} diff --git a/ShenYin/ShenYin/UI/Mine/Feedback/Commit/FeedbackCommitVC.swift b/ShenYin/ShenYin/UI/Mine/Feedback/Commit/FeedbackCommitVC.swift index bc73177..48c92de 100644 --- a/ShenYin/ShenYin/UI/Mine/Feedback/Commit/FeedbackCommitVC.swift +++ b/ShenYin/ShenYin/UI/Mine/Feedback/Commit/FeedbackCommitVC.swift @@ -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 index 0000000..9f64b66 --- /dev/null +++ b/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginDetailVC.swift @@ -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 index 0000000..848ac51 --- /dev/null +++ b/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginRecordModel.swift @@ -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 index 0000000..57d5518 --- /dev/null +++ b/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginVC.swift @@ -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 index 0000000..3001ffd --- /dev/null +++ b/ShenYin/ShenYin/UI/Mine/LoginManager/DeviceLoginVM.swift @@ -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) + } + } +} diff --git a/ShenYin/ShenYin/UI/Mine/Root/MineHeaderView.swift b/ShenYin/ShenYin/UI/Mine/Root/MineHeaderView.swift index 46918f9..32c4745 100644 --- a/ShenYin/ShenYin/UI/Mine/Root/MineHeaderView.swift +++ b/ShenYin/ShenYin/UI/Mine/Root/MineHeaderView.swift @@ -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()) } } diff --git a/ShenYin/ShenYin/UI/Mine/Root/MineVC.swift b/ShenYin/ShenYin/UI/Mine/Root/MineVC.swift index 0357c07..070b62c 100644 --- a/ShenYin/ShenYin/UI/Mine/Root/MineVC.swift +++ b/ShenYin/ShenYin/UI/Mine/Root/MineVC.swift @@ -40,6 +40,9 @@ class MineVC: BaseVC{ CacheManager.shared.user.getStorageSize { result in + } + DeviceVM.shared.refresh { result in + } } override func viewDidAppear(_ animated: Bool) { diff --git a/ShenYin/ShenYin/UI/Mine/UserInfo/CustomCell1.swift b/ShenYin/ShenYin/UI/Mine/UserInfo/CustomCell1.swift index a5f0957..36c8a8d 100644 --- a/ShenYin/ShenYin/UI/Mine/UserInfo/CustomCell1.swift +++ b/ShenYin/ShenYin/UI/Mine/UserInfo/CustomCell1.swift @@ -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 "检查更新" diff --git a/ShenYin/ShenYin/UI/Sleep/AudioPlayerVC.swift b/ShenYin/ShenYin/UI/Sleep/AudioPlayerVC.swift index 9a8abb5..82d2d3c 100644 --- a/ShenYin/ShenYin/UI/Sleep/AudioPlayerVC.swift +++ b/ShenYin/ShenYin/UI/Sleep/AudioPlayerVC.swift @@ -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 } } diff --git a/ShenYin/ShenYin/UI/common/Cache/CacheManager.swift b/ShenYin/ShenYin/UI/common/Cache/CacheManager.swift index 373b507..9a0b2c2 100644 --- a/ShenYin/ShenYin/UI/common/Cache/CacheManager.swift +++ b/ShenYin/ShenYin/UI/common/Cache/CacheManager.swift @@ -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 index 0000000..69c2455 --- /dev/null +++ b/ShenYin/ShenYin/UI/common/CommonCell.swift @@ -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) + } + } + + +} diff --git a/ShenYin/ShenYin/UI/common/CustomButton.swift b/ShenYin/ShenYin/UI/common/CustomButton.swift index a9ffd99..1f7834e 100644 --- a/ShenYin/ShenYin/UI/common/CustomButton.swift +++ b/ShenYin/ShenYin/UI/common/CustomButton.swift @@ -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 } } + } diff --git a/ShenYin/ShenYin/UI/common/pop/ConnectPopView.swift b/ShenYin/ShenYin/UI/common/pop/ConnectPopView.swift index 82879e3..b93ec1e 100644 --- a/ShenYin/ShenYin/UI/common/pop/ConnectPopView.swift +++ b/ShenYin/ShenYin/UI/common/pop/ConnectPopView.swift @@ -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?() +// } +// } }