From 3d8ce4866799bea7e66699acdeb86b60b0ba033c Mon Sep 17 00:00:00 2001 From: 无故事王国 <841720330@qq.com> Date: 星期一, 03 六月 2024 19:51:06 +0800 Subject: [PATCH] add --- DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketTagCCell.xib | 65 + DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketCCell.swift | 10 DolphinEnglishLearnStudent/ViewModel/UserViewModel.swift | 55 + DolphinEnglishLearnStudent/Moudle/Home/CCell/AwardListCCell.xib | 22 DolphinEnglishLearnStudent/Moudle/Me/TCell/Home_1_TCell.swift | 20 DolphinEnglishLearnStudent/Moudle/Me/VC/CoinRecordHistoryVC.swift | 26 DolphinEnglishLearnStudent/Login/LoginVC.xib | 2 DolphinEnglishLearnStudent/ViewModel/RefreshModel.swift | 305 ++++++ DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketTagCCell.swift | 20 DolphinEnglishLearnStudent/Moudle/Me/MeVC.xib | 3 DolphinEnglishLearnStudent/Moudle/Market/VC/MarketContentVC.xib | 28 DolphinEnglishLearnStudent/Moudle/Market/VC/MarketExchangeVC.swift | 100 ++ DolphinEnglishLearnStudent/Moudle/Home/View/AwardListView.swift | 28 DolphinEnglishLearnStudent/Moudle/Home/HomeVC.swift | 10 DolphinEnglishLearnStudent/Other/UIView/CommonBannerView.swift | 208 ++++ DolphinEnglishLearnStudent/Services/Services.swift | 213 ++++ DolphinEnglishLearnStudent/Moudle/Me/TCell/AddressManageTCell.xib | 15 DolphinEnglishLearnStudent/Moudle/Market/VC/MarketContentVC.swift | 98 ++ DolphinEnglishLearnStudent/Moudle/Me/TCell/AddressManageTCell.swift | 39 DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenMenuVC.swift | 18 DolphinEnglishLearnStudent/Other/UIView/CityAddressPickerView.swift | 248 +++++ DolphinEnglishLearnStudent/Moudle/Market/MarketVC.xib | 1 DolphinEnglishLearnStudent/Moudle/Market/VC/MarketExchangeVC.xib | 73 + DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageVC.swift | 26 DolphinEnglishLearnStudent/SceneDelegate.swift | 4 DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.swift | 29 DolphinEnglishLearnStudent/Moudle/Me/MeVC.swift | 19 DolphinEnglishLearnStudent/Moudle/Me/TCell/Home_1_TCell.xib | 10 DolphinEnglishLearnStudent/Login/LoginVC.swift | 18 Podfile | 1 DolphinEnglishLearnStudent/Moudle/Me/VC/ExchangeRecordHistoryVC.swift | 14 DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageHandleVC.swift | 102 ++ DolphinEnglishLearnStudent/Services/NetworkRequest.swift | 278 ++++++ DolphinEnglishLearnStudent/Config/Config.swift | 34 DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageHandleVC.xib | 13 DolphinEnglishLearnStudent/Models/CommonModel.swift | 237 +++++ DolphinEnglishLearnStudent/Moudle/Market/MarketVC.swift | 109 ++ DolphinEnglishLearnStudent/Moudle/Me/VC/StudyVC.swift | 18 DolphinEnglishLearnStudent/Moudle/Me/VC/CoinRecordHistoryVC.xib | 3 DolphinEnglishLearnStudent/Other/CommonWebVC.swift | 32 DolphinEnglishLearnStudent.xcodeproj/project.pbxproj | 68 + DolphinEnglishLearnStudent/Moudle/Home/CCell/AwardListCCell.swift | 7 DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.xib | 26 43 files changed, 2,528 insertions(+), 127 deletions(-) diff --git a/DolphinEnglishLearnStudent.xcodeproj/project.pbxproj b/DolphinEnglishLearnStudent.xcodeproj/project.pbxproj index 002fec5..03e9779 100644 --- a/DolphinEnglishLearnStudent.xcodeproj/project.pbxproj +++ b/DolphinEnglishLearnStudent.xcodeproj/project.pbxproj @@ -58,6 +58,13 @@ 1319B0292C0818540052F889 /* HomeListenStory_2_VC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1319B0282C0818540052F889 /* HomeListenStory_2_VC.swift */; }; 1319B02C2C081A320052F889 /* SimpleImageCCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1319B02A2C081A320052F889 /* SimpleImageCCell.swift */; }; 1319B02D2C081A320052F889 /* SimpleImageCCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1319B02B2C081A320052F889 /* SimpleImageCCell.xib */; }; + 1319B0312C0859370052F889 /* Services.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1319B02F2C0859370052F889 /* Services.swift */; }; + 1319B0322C0859370052F889 /* NetworkRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1319B0302C0859370052F889 /* NetworkRequest.swift */; }; + 1319B0352C0859E20052F889 /* UserViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1319B0342C0859E20052F889 /* UserViewModel.swift */; }; + 131C030A2C0D564000EA4C25 /* MarketTagCCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 131C03092C0D564000EA4C25 /* MarketTagCCell.xib */; }; + 131C030B2C0D564000EA4C25 /* MarketTagCCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 131C03082C0D564000EA4C25 /* MarketTagCCell.swift */; }; + 131C030D2C0D6A4800EA4C25 /* CommonBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 131C030C2C0D6A4800EA4C25 /* CommonBannerView.swift */; }; + 131C03112C0DA5D500EA4C25 /* CityAddressPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 131C03102C0DA5D500EA4C25 /* CityAddressPickerView.swift */; }; 133386382C007E91002EE788 /* HomeListenFight_lesson_2_VC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133386372C007E91002EE788 /* HomeListenFight_lesson_2_VC.swift */; }; 13397D962C05EA9D003440F9 /* ListenFight_Game_CCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13397D942C05EA9D003440F9 /* ListenFight_Game_CCell.swift */; }; 13397D972C05EA9D003440F9 /* ListenFight_Game_CCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13397D952C05EA9D003440F9 /* ListenFight_Game_CCell.xib */; }; @@ -81,6 +88,8 @@ 13A830FA2C043A0600BB2F23 /* Lesson_3_AnswerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13A830F92C043A0600BB2F23 /* Lesson_3_AnswerView.xib */; }; 13AA25392C00759600F075B3 /* HomeStudyCompleteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13AA25372C00759600F075B3 /* HomeStudyCompleteVC.swift */; }; 13AA253A2C00759600F075B3 /* HomeStudyCompleteVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13AA25382C00759600F075B3 /* HomeStudyCompleteVC.xib */; }; + 13CD3AC72C0874B3007E1065 /* CommonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CD3AC62C0874B3007E1065 /* CommonModel.swift */; }; + 13CD3AC92C0886E5007E1065 /* RefreshModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CD3AC82C0886E5007E1065 /* RefreshModel.swift */; }; 13CDF4492C0566E400E8D4FD /* HomeListenFight_lesson_4_VC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CDF4482C0566E400E8D4FD /* HomeListenFight_lesson_4_VC.swift */; }; 13CDF44C2C056A6900E8D4FD /* ListenFight_lesson_4_CCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13CDF44B2C056A6900E8D4FD /* ListenFight_lesson_4_CCell.xib */; }; 13CDF44D2C056A6900E8D4FD /* ListenFight_lesson_4_CCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CDF44A2C056A6900E8D4FD /* ListenFight_lesson_4_CCell.swift */; }; @@ -157,6 +166,13 @@ 1319B0282C0818540052F889 /* HomeListenStory_2_VC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeListenStory_2_VC.swift; sourceTree = "<group>"; }; 1319B02A2C081A320052F889 /* SimpleImageCCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleImageCCell.swift; sourceTree = "<group>"; }; 1319B02B2C081A320052F889 /* SimpleImageCCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SimpleImageCCell.xib; sourceTree = "<group>"; }; + 1319B02F2C0859370052F889 /* Services.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Services.swift; sourceTree = "<group>"; }; + 1319B0302C0859370052F889 /* NetworkRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkRequest.swift; sourceTree = "<group>"; }; + 1319B0342C0859E20052F889 /* UserViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserViewModel.swift; sourceTree = "<group>"; }; + 131C03082C0D564000EA4C25 /* MarketTagCCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketTagCCell.swift; sourceTree = "<group>"; }; + 131C03092C0D564000EA4C25 /* MarketTagCCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MarketTagCCell.xib; sourceTree = "<group>"; }; + 131C030C2C0D6A4800EA4C25 /* CommonBannerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonBannerView.swift; sourceTree = "<group>"; }; + 131C03102C0DA5D500EA4C25 /* CityAddressPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityAddressPickerView.swift; sourceTree = "<group>"; }; 133386372C007E91002EE788 /* HomeListenFight_lesson_2_VC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeListenFight_lesson_2_VC.swift; sourceTree = "<group>"; }; 13397D942C05EA9D003440F9 /* ListenFight_Game_CCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListenFight_Game_CCell.swift; sourceTree = "<group>"; }; 13397D952C05EA9D003440F9 /* ListenFight_Game_CCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ListenFight_Game_CCell.xib; sourceTree = "<group>"; }; @@ -180,6 +196,8 @@ 13A830F92C043A0600BB2F23 /* Lesson_3_AnswerView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Lesson_3_AnswerView.xib; sourceTree = "<group>"; }; 13AA25372C00759600F075B3 /* HomeStudyCompleteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeStudyCompleteVC.swift; sourceTree = "<group>"; }; 13AA25382C00759600F075B3 /* HomeStudyCompleteVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HomeStudyCompleteVC.xib; sourceTree = "<group>"; }; + 13CD3AC62C0874B3007E1065 /* CommonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonModel.swift; sourceTree = "<group>"; }; + 13CD3AC82C0886E5007E1065 /* RefreshModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshModel.swift; sourceTree = "<group>"; }; 13CDF4482C0566E400E8D4FD /* HomeListenFight_lesson_4_VC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeListenFight_lesson_4_VC.swift; sourceTree = "<group>"; }; 13CDF44A2C056A6900E8D4FD /* ListenFight_lesson_4_CCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListenFight_lesson_4_CCell.swift; sourceTree = "<group>"; }; 13CDF44B2C056A6900E8D4FD /* ListenFight_lesson_4_CCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ListenFight_lesson_4_CCell.xib; sourceTree = "<group>"; }; @@ -235,6 +253,9 @@ 130278282BFD957100DDCE81 /* DolphinEnglishLearnStudent */ = { isa = PBXGroup; children = ( + 13CD3AC52C08749E007E1065 /* Models */, + 1319B0332C0859D70052F889 /* ViewModel */, + 1319B02E2C08592D0052F889 /* Services */, 130278632BFD9E5D00DDCE81 /* Moudle */, 130278582BFD985E00DDCE81 /* Other */, 1302784E2BFD97EF00DDCE81 /* Login */, @@ -275,6 +296,7 @@ 130278472BFD979200DDCE81 /* Base */ = { isa = PBXGroup; children = ( + 1319B0362C085B040052F889 /* View */, 130278432BFD979200DDCE81 /* BaseNav.swift */, 130278442BFD979200DDCE81 /* BaseTabBarVC.swift */, 130278452BFD979200DDCE81 /* BaseVC.swift */, @@ -295,6 +317,7 @@ 130278562BFD985E00DDCE81 /* UIView */ = { isa = PBXGroup; children = ( + 131C030C2C0D6A4800EA4C25 /* CommonBannerView.swift */, 130278512BFD985E00DDCE81 /* BitrhdayPickerView.swift */, 130278522BFD985E00DDCE81 /* CommonAlertView.swift */, 130278532BFD985E00DDCE81 /* CommonAlertView.xib */, @@ -302,6 +325,7 @@ 130278552BFD985E00DDCE81 /* CommonInputView.xib */, 1362C75E2BFF4BA900BD7F73 /* StudyHandleView.swift */, 1362C7602BFF4BB100BD7F73 /* StudyHandleView.xib */, + 131C03102C0DA5D500EA4C25 /* CityAddressPickerView.swift */, ); path = UIView; sourceTree = "<group>"; @@ -352,6 +376,8 @@ children = ( 1302786A2BFD9ED600DDCE81 /* MarketCCell.swift */, 1302786B2BFD9ED600DDCE81 /* MarketCCell.xib */, + 131C03082C0D564000EA4C25 /* MarketTagCCell.swift */, + 131C03092C0D564000EA4C25 /* MarketTagCCell.xib */, ); path = CCell; sourceTree = "<group>"; @@ -395,6 +421,31 @@ 130278902BFD9FBF00DDCE81 /* Home_1_TCell.xib */, ); path = TCell; + sourceTree = "<group>"; + }; + 1319B02E2C08592D0052F889 /* Services */ = { + isa = PBXGroup; + children = ( + 1319B0302C0859370052F889 /* NetworkRequest.swift */, + 1319B02F2C0859370052F889 /* Services.swift */, + ); + path = Services; + sourceTree = "<group>"; + }; + 1319B0332C0859D70052F889 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 13CD3AC82C0886E5007E1065 /* RefreshModel.swift */, + 1319B0342C0859E20052F889 /* UserViewModel.swift */, + ); + path = ViewModel; + sourceTree = "<group>"; + }; + 1319B0362C085B040052F889 /* View */ = { + isa = PBXGroup; + children = ( + ); + path = View; sourceTree = "<group>"; }; 13649E992C002FDF001B04E2 /* CCell */ = { @@ -456,6 +507,14 @@ 13AA25382C00759600F075B3 /* HomeStudyCompleteVC.xib */, ); path = VC; + sourceTree = "<group>"; + }; + 13CD3AC52C08749E007E1065 /* Models */ = { + isa = PBXGroup; + children = ( + 13CD3AC62C0874B3007E1065 /* CommonModel.swift */, + ); + path = Models; sourceTree = "<group>"; }; 13EEB88F2BFED3C6002996FC /* View */ = { @@ -607,6 +666,7 @@ 13EEB8A92BFF354B002996FC /* HomeListen_item_TCell.xib in Resources */, 13CDF4512C05757400E8D4FD /* Lesson_4_AnswerView.xib in Resources */, 1319B02D2C081A320052F889 /* SimpleImageCCell.xib in Resources */, + 131C030A2C0D564000EA4C25 /* MarketTagCCell.xib in Resources */, 130278982BFD9FBF00DDCE81 /* GoodsItemTCell.xib in Resources */, 13CDF44C2C056A6900E8D4FD /* ListenFight_lesson_4_CCell.xib in Resources */, 130278962BFD9FBF00DDCE81 /* Home_1_TCell.xib in Resources */, @@ -669,12 +729,19 @@ 130278992BFD9FBF00DDCE81 /* Home_1_TCell.swift in Sources */, 1302784B2BFD979200DDCE81 /* TapBtn.swift in Sources */, 13CDF44F2C05756C00E8D4FD /* Lesson_4_AnswerView.swift in Sources */, + 1319B0312C0859370052F889 /* Services.swift in Sources */, 13EEB8A82BFF354B002996FC /* HomeListen_item_TCell.swift in Sources */, 13A830F12C04196400BB2F23 /* HomeListenFight_lesson_3_VC.swift in Sources */, 13CDF44D2C056A6900E8D4FD /* ListenFight_lesson_4_CCell.swift in Sources */, + 131C030B2C0D564000EA4C25 /* MarketTagCCell.swift in Sources */, + 13CD3AC72C0874B3007E1065 /* CommonModel.swift in Sources */, + 131C030D2C0D6A4800EA4C25 /* CommonBannerView.swift in Sources */, + 1319B0322C0859370052F889 /* NetworkRequest.swift in Sources */, 1362C75F2BFF4BA900BD7F73 /* StudyHandleView.swift in Sources */, 130278672BFD9E8C00DDCE81 /* HomeVC.swift in Sources */, + 131C03112C0DA5D500EA4C25 /* CityAddressPickerView.swift in Sources */, 130278482BFD979200DDCE81 /* BaseNav.swift in Sources */, + 1319B0352C0859E20052F889 /* UserViewModel.swift in Sources */, 130278622BFD999200DDCE81 /* LoginVC.swift in Sources */, 13EEB89C2BFF1C35002996FC /* HomeListenMenuVC.swift in Sources */, 13CDF4492C0566E400E8D4FD /* HomeListenFight_lesson_4_VC.swift in Sources */, @@ -703,6 +770,7 @@ 1302788D2BFD9F4200DDCE81 /* CoinRecordHistoryVC.swift in Sources */, 13A049FF2C058B1400F1F52E /* HomeListenFight_lesson_5_VC.swift in Sources */, 1302785A2BFD985E00DDCE81 /* CommonAlertView.swift in Sources */, + 13CD3AC92C0886E5007E1065 /* RefreshModel.swift in Sources */, 1302782C2BFD957100DDCE81 /* SceneDelegate.swift in Sources */, 1302787D2BFD9ED600DDCE81 /* MarketExchangeVC.swift in Sources */, 1302789A2BFD9FBF00DDCE81 /* AddressManageTCell.swift in Sources */, diff --git a/DolphinEnglishLearnStudent/Config/Config.swift b/DolphinEnglishLearnStudent/Config/Config.swift index ca04b6a..f37c501 100644 --- a/DolphinEnglishLearnStudent/Config/Config.swift +++ b/DolphinEnglishLearnStudent/Config/Config.swift @@ -32,10 +32,35 @@ } +func LogSuccess(_ items:Any...,separator:String=" ",file:String=#file,function:String=#function,line:Int=#line){ +#if DEBUG + if #available(iOS 14.0, *) { + let logger = Logger(subsystem: "English", category: function) + logger.error("\(items)") + }else{ + let file = (file as NSString).lastPathComponent.split(separator: ".").first!; + print("✅✅✅ SUCCESS: \(file) \(function) [Line: \(line)]: \(items)",separator); + } + +#endif +} + +func LogError(_ items:Any...,separator:String=" ",file:String=#file,function:String=#function,line:Int=#line){ +#if DEBUG + if #available(iOS 14.0, *) { + let logger = Logger(subsystem: "English", category: function) + logger.error("\(items)") + }else{ + let file = (file as NSString).lastPathComponent.split(separator: ".").first!; + print("❌❌❌ ERROR: \(file) \(function) [Line: \(line)]: \(items)",separator); + } +#endif +} + func LogInfo(_ items:Any...,separator:String=" ",file:String=#file,function:String=#function,line:Int=#line){ #if DEBUG if #available(iOS 14.0, *) { - let logger = Logger(subsystem: "WanPai", category: function) + let logger = Logger(subsystem: "English", category: function) logger.error("\(items)") }else{ let file = (file as NSString).lastPathComponent.split(separator: ".").first!; @@ -44,6 +69,13 @@ #endif } +func LogResponse(_ items:Any...,separator:String=" ",file:String=#file,function:String=#function,line:Int=#line){ +#if DEBUG + print("返回数据") + print(items); +#endif +} + //提示框 func alert(msg: String) { SVProgressHUD.showInfo(withStatus: msg) diff --git a/DolphinEnglishLearnStudent/Login/LoginVC.swift b/DolphinEnglishLearnStudent/Login/LoginVC.swift index 21da0cb..6b2dcd1 100644 --- a/DolphinEnglishLearnStudent/Login/LoginVC.swift +++ b/DolphinEnglishLearnStudent/Login/LoginVC.swift @@ -41,7 +41,7 @@ alert(msg: "请输入验证码");return false } - guard tf_phone.text!.count != 6 else { + guard tf_authCode.text!.count == 6 else { alert(msg: "请输入6位验证码");return false } return true @@ -54,14 +54,14 @@ /// 隐私协议 @IBAction func privacyAction(_ sender: UIButton) { - let vc = CommonWebVC(type: .privacyAgreement) + let vc = CommonWebVC(type: .PrivacyPolicy) vc.title = "隐私协议" self.navigationController?.pushViewController(vc, animated: true) } /// 用户协议 @IBAction func privacyUserAction(_ sender: UIButton) { - let vc = CommonWebVC(type: .userAgreement) + let vc = CommonWebVC(type: .UserProtocol) vc.title = "用户协议" self.navigationController?.pushViewController(vc, animated: true) } @@ -69,7 +69,9 @@ @IBAction func getCodeAction(_ sender: UIButton) { guard authInputPhone() else {return} - sender.openCountDown(60, defultTitle: "获取验证码", textColor:UIColor(hexStr: "#41A2EB"), unenableColor: .gray) + Services.sendPhoneCode(phone: tf_phone.text!).subscribe(onNext: {_ in + sender.openCountDown(60, defultTitle: "获取验证码", textColor:UIColor(hexStr: "#41A2EB"), unenableColor: .gray) + }).disposed(by: disposeBag) } @IBAction func loginAction(_ sender: UIButton) { @@ -81,6 +83,12 @@ alert(msg: "请阅读并同意《隐私协议》《用户协议》");return } - sceneDelegate?.loginSuccess() + Services.login(phone: tf_phone.text!, code: tf_authCode.text!).subscribe(onNext: {result in + if var token = result.data?.token{ + token.request_time = Int(Date().timeIntervalSince1970) + LoginTokenModel.saveToken(token) + sceneDelegate?.loginSuccess() + } + }).disposed(by: disposeBag) } } diff --git a/DolphinEnglishLearnStudent/Login/LoginVC.xib b/DolphinEnglishLearnStudent/Login/LoginVC.xib index bd86a55..b58e11e 100644 --- a/DolphinEnglishLearnStudent/Login/LoginVC.xib +++ b/DolphinEnglishLearnStudent/Login/LoginVC.xib @@ -131,6 +131,7 @@ </constraints> <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> <state key="normal" image="btn_choose_u"/> + <state key="selected" image="btn_choose"/> <connections> <action selector="chooseAction:" destination="-1" eventType="touchUpInside" id="taa-1D-MNJ"/> </connections> @@ -210,6 +211,7 @@ </objects> <resources> <image name="bg_login" width="296" height="129"/> + <image name="btn_choose" width="28" height="28"/> <image name="btn_choose_u" width="28" height="28"/> <image name="icon_input_code" width="15" height="18"/> <image name="icon_input_phone" width="16" height="18"/> diff --git a/DolphinEnglishLearnStudent/Models/CommonModel.swift b/DolphinEnglishLearnStudent/Models/CommonModel.swift new file mode 100644 index 0000000..b41fefc --- /dev/null +++ b/DolphinEnglishLearnStudent/Models/CommonModel.swift @@ -0,0 +1,237 @@ +// +// CommonModel.swift +// DolphinEnglishLearnStudent +// +// Created by 无故事王国 on 2024/5/30. +// + +import Foundation +import HandyJSON +import UserDefaultsStore + +struct LoginModel:HandyJSON{ + var token:LoginTokenModel? +} + +struct LoginTokenModel:HandyJSON,Identifiable,Codable{ + static let idKey = \LoginTokenModel.id + var id: Int = 0 + + var access_token = "" + var expires_in = 0 + var request_time = 0 //请求时间 + + private static let userInfo = UserDefaultsStore<LoginTokenModel>(uniqueIdentifier: "LoginTokenModel")! + + static func saveToken(_ model:LoginTokenModel){ + do{ + try LoginTokenModel.userInfo.save(model) + }catch{ + + } + } + + static func isOverdue()->Bool{ + if let token = LoginTokenModel.getToken(){ + //过期时间(秒) + let overdueTimeval = token.expires_in * 60 + token.request_time + + if overdueTimeval < Int(Date().timeIntervalSince1970){ + return true + } + return false + } + return true + } + + static func getToken()->LoginTokenModel?{ + return LoginTokenModel.userInfo.allObjects().first + } + + static func clearToken(){ + LoginTokenModel.userInfo.deleteAll() + } +} + + +struct RecommendModel:HandyJSON{ + var basicCount: Int = 0 + var coverImg: String = "" + var createBy: String = "" + var createTime: String = "" + var detail: String = "" + var detailImg: String = "" + var disabled: Bool = false + var id: Int = 0 + var insertTime: String = "" + var integral: Int = 0 + var inventory: Int = 0 + var isDelete: Int = 0 + var name: String = "" + var price: Int = 0 + var surplus: Int = 0 + var total: Int = 0 + var type: Int = 0 + var typeIds: String = "" + var updateBy: String = "" + var updateTime: String = "" + var userCount: Int = 0 +} + +struct MarketModel:HandyJSON{ + var basicCount: Int = 0 + var coverImg: String = "" + var createBy: String = "" + var createTime: String = "" + var detail: String = "" + var detailImg: String = "" + var disabled: Bool = false + var id: Int = 0 + var integral: Int = 0 + var inventory: Int = 0 + var isDelete: Int = 0 + var name: String = "" + var price: Int = 0 + var surplus: Int = 0 + var total: Int = 0 + var type: Int = 0 + var typeIds: String = "" + var updateBy: String = "" + var updateTime: String = "" + var userCount: Int = 0 +} + +struct MarketTypeModel:HandyJSON,Hashable{ + var id = 0 + var name = "" +} + +struct MarketDetailModel:HandyJSON{ + var exchangeNumber: Int = 0 + var good: MarketModel? + var goodTypes = [MarketTypeModel]() + var orderNumber: String = "" + var recipient: MarketRecipientModel? +} + +struct MarketRecipientModel:HandyJSON,Hashable{ + var id = 0 + var name = "" +} + +struct AddressModel:HandyJSON{ + var address: String = "" + var city: String = "" + var cityCode: String = "" + var createBy: String = "" + var createTime: String = "" + var disabled: Bool = false + var id: Int = 0 + var isDefault: Int = 0 + var province: String = "" + var provinceCode: String = "" + var recipient: String = "" + var recipientPhone: String = "" + var updateBy: String = "" + var updateTime: String = "" + var userId: Int = 0 +} + +struct AddressTreeModel:HandyJSON{ + var id = 0 + var name = "" + var code = "" + var parentId = 0 + var children:[AddressTreeModel]? +} + +struct IntegralModel:HandyJSON{ + var createBy: String = "" + var createTime: String = "" + var disabled: Bool = false + var gameId: Int = 0 + var id: Int = 0 + var integral: String = "" + var method: String = "" + var storyId: Int = 0 + var type: String = "" + var updateBy: String = "" + var updateTime: String = "" + var userId: Int = 0 +} + +struct ExchangeRecordModel:HandyJSON{ + var completeTime: String = "" + var consigneeAddress: String = "" + var consigneeName: String = "" + var consigneePhone: String = "" + var count: Int = 0 + var createBy: String = "" + var createTime: String = "" + var disabled: Bool = false + var express: String = "" + var expressNumber: String = "" + var expressTime: String = "" + var goodsId: Int = 0 + var id: Int = 0 + var insertTime: String = "" + var integral: Int = 0 + var orderNumber: String = "" + var state: Int = 0 + var updateBy: String = "" + var updateTime: String = "" + var userId: Int = 0 +} + +struct StudyGamesModel:HandyJSON{ + var gameRecordList = [StudyGamesRecordModel]() + var record:StudyDataRecordModel? +} + + +struct StudyGamesRecordModel:HandyJSON{ + var accuracy: Int = 0 + var createBy: String? + var createTime: String? + var disabled: Bool = false + var gameDifficulty: Int = 0 + var gameId: Int = 0 + var gameName: String? + var id: Int = 0 + var updateBy: String? + var updateTime: String? + var userId: Int = 0 + var useTime: Int = 0 +} + +struct StudyDataRecordModel:HandyJSON{ + var answer: Int = 0 + var createBy: String? + var createTime: String? + var day: Int = 0 + var disabled: Bool = false + var id: Int = 0 + var induction: Int = 0 + var listen: Int = 0 + var look: Int = 0 + var monthStudy: Int = 0 + var pair: Int = 0 + var surplus: Int = 0 + var todayStudy: Int = 0 + var totalStudy: Int = 0 + var updateBy: String? + var updateTime: String? + var userId: Int = 0 + var week: Int = 0 + var weekStudy: Int = 0 +} + +struct ListenWeekModel:HandyJSON{ + var id = 0 + var day = 0 + var quarter = 0 + var title = "" + var totalIntegral = 0 + var type = 0 + var week = 0 +} diff --git a/DolphinEnglishLearnStudent/Moudle/Home/CCell/AwardListCCell.swift b/DolphinEnglishLearnStudent/Moudle/Home/CCell/AwardListCCell.swift index d0f2c1f..5f87507 100644 --- a/DolphinEnglishLearnStudent/Moudle/Home/CCell/AwardListCCell.swift +++ b/DolphinEnglishLearnStudent/Moudle/Home/CCell/AwardListCCell.swift @@ -11,11 +11,16 @@ class AwardListCCell: UICollectionViewCell { @IBOutlet weak var image_awar: UIImageView! + @IBOutlet weak var label_title: UILabel! override func awakeFromNib() { super.awakeFromNib() - // Initialization code } + func setModel(_ model:RecommendModel){ + image_awar.sd_setImage(with: URL(string: model.coverImg)) + image_awar.jq_cornerRadius = 8 + label_title.text = model.name + } override func layoutSubviews() { super.layoutSubviews() diff --git a/DolphinEnglishLearnStudent/Moudle/Home/CCell/AwardListCCell.xib b/DolphinEnglishLearnStudent/Moudle/Home/CCell/AwardListCCell.xib index c926478..a185c7f 100644 --- a/DolphinEnglishLearnStudent/Moudle/Home/CCell/AwardListCCell.xib +++ b/DolphinEnglishLearnStudent/Moudle/Home/CCell/AwardListCCell.xib @@ -10,21 +10,30 @@ <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="_AwardListCCell" id="gTV-IL-0wX" customClass="AwardListCCell" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="276" height="526"/> + <rect key="frame" x="0.0" y="0.0" width="243" height="526"/> <autoresizingMask key="autoresizingMask"/> <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center"> - <rect key="frame" x="0.0" y="0.0" width="276" height="526"/> + <rect key="frame" x="0.0" y="0.0" width="243" height="526"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <subviews> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="YbH-L3-L1g"> - <rect key="frame" x="0.0" y="0.0" width="276" height="359"/> + <rect key="frame" x="0.0" y="0.0" width="243" height="316"/> <color key="backgroundColor" red="0.61960784310000006" green="0.54117647059999996" blue="0.85882352939999995" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <constraints> <constraint firstAttribute="width" secondItem="YbH-L3-L1g" secondAttribute="height" multiplier="1:1.3" id="K9A-Ng-kLe"/> </constraints> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="boolean" keyPath="ld_maskToBoundsXIB" value="YES"/> + <userDefinedRuntimeAttribute type="number" keyPath="ld_cornerRadiusXIB"> + <real key="value" value="8"/> + </userDefinedRuntimeAttribute> + </userDefinedRuntimeAttributes> </imageView> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wPP-pc-amm"> - <rect key="frame" x="0.0" y="368" width="276" height="19.5"/> + <rect key="frame" x="0.0" y="325" width="243" height="44"/> + <constraints> + <constraint firstAttribute="height" constant="44" id="7Eh-PB-yFf"/> + </constraints> <fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="sRGB"/> <nil key="highlightedColor"/> @@ -39,11 +48,12 @@ <constraint firstAttribute="trailing" secondItem="wPP-pc-amm" secondAttribute="trailing" id="UHp-wV-FRO"/> <constraint firstItem="YbH-L3-L1g" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="WD8-o4-ZS3"/> </constraints> - <size key="customSize" width="276" height="526"/> + <size key="customSize" width="243" height="526"/> <connections> <outlet property="image_awar" destination="YbH-L3-L1g" id="kuW-TO-4BH"/> + <outlet property="label_title" destination="wPP-pc-amm" id="vw9-1s-r0k"/> </connections> - <point key="canvasLocation" x="130.2439024390244" y="140.84745762711864"/> + <point key="canvasLocation" x="116.70731707317073" y="140.84745762711864"/> </collectionViewCell> </objects> </document> diff --git a/DolphinEnglishLearnStudent/Moudle/Home/HomeVC.swift b/DolphinEnglishLearnStudent/Moudle/Home/HomeVC.swift index 1715ba3..77e0951 100644 --- a/DolphinEnglishLearnStudent/Moudle/Home/HomeVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Home/HomeVC.swift @@ -11,10 +11,12 @@ override func viewDidLoad() { super.viewDidLoad() - - AwardListView.show { - - } + Services.goodRecommend().subscribe(onNext: { data in + AwardListView.show(items: data.data ?? []) {[weak self] model in + let vc = MarketContentVC(goodsId: model.id) + self?.push(vc: vc) + } + }).disposed(by: disposeBag) } @IBAction func listenAction(_ sender: UIButton) { diff --git a/DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenMenuVC.swift b/DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenMenuVC.swift index d22fedc..366d310 100644 --- a/DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenMenuVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenMenuVC.swift @@ -9,15 +9,27 @@ class HomeListenMenuVC: BaseVC { + @IBOutlet weak var tableView: UITableView! @IBOutlet weak var collectionView: UICollectionView! + private var repeatColors = ["#F8A169","#92CADB","#9E8ADB","#6DD1BA","#37C06E","#DEB975","#C54A59","#5DA0D3","#F0C433","#DC4827"] private var titleItems = ["第一季","第二季","第三季","第四季"] private var selectIndexPath:IndexPath = IndexPath(row: 0, section: 0) + private var dataItems = Array<[ListenWeekModel]>(repeating: [], count: 4) + override func viewDidLoad() { super.viewDidLoad() + getData() + } + + private func getData(){ + Services.weekList(quarter: selectIndexPath.row).subscribe(onNext: {result in + self.dataItems[self.selectIndexPath.row] = result.data ?? [] + self.collectionView.reloadData() + }).disposed(by: disposeBag) } override func setUI() { @@ -53,7 +65,7 @@ } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return 20 + return dataItems[selectIndexPath.row].count } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { @@ -74,6 +86,10 @@ extension HomeListenMenuVC:UITableViewDataSource,UITableViewDelegate{ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { selectIndexPath = indexPath + + if dataItems[indexPath.row].count == 0{ + getData() + } tableView.reloadData() } diff --git a/DolphinEnglishLearnStudent/Moudle/Home/View/AwardListView.swift b/DolphinEnglishLearnStudent/Moudle/Home/View/AwardListView.swift index 3086483..62af126 100644 --- a/DolphinEnglishLearnStudent/Moudle/Home/View/AwardListView.swift +++ b/DolphinEnglishLearnStudent/Moudle/Home/View/AwardListView.swift @@ -11,6 +11,8 @@ @IBOutlet weak var collectionView: UICollectionView! @IBOutlet weak var view_container: UIView! + private var items = [RecommendModel]() + private var clickClouse:((RecommendModel)->Void)! override func awakeFromNib() { super.awakeFromNib() self.alpha = 0 @@ -22,8 +24,13 @@ layoutIfNeeded() } - static func show(clouse:@escaping ()->Void){ + static func show(items:[RecommendModel],clouse:@escaping (RecommendModel)->Void){ + + if items.count == 0{return} + let awardListView = AwardListView.jq_loadNibView() + awardListView.items = items + awardListView.clickClouse = clouse sceneDelegate?.window?.addSubview(awardListView) awardListView.frame = sceneDelegate?.window?.frame ?? .zero @@ -31,6 +38,8 @@ awardListView.alpha = 1 awardListView.view_container.transform = .init(translationX: 1.0, y: 1.0) awardListView.layoutIfNeeded() + }completion: { _ in + awardListView.collectionView.reloadData() } } @@ -45,23 +54,36 @@ } extension AwardListView:UICollectionViewDelegate{ + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + + UIView.animate(withDuration: 0.4) { + self.alpha = 0 + self.view_container.transform = .init(scaleX: 0.1, y: 0.1) + } completion: { _ in + self.removeFromSuperview() + let item = self.items[indexPath.row] + self.clickClouse(item) + } + } } extension AwardListView:UICollectionViewDataSource{ func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let item = items[indexPath.row] let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_AwardListCCell", for: indexPath) as! AwardListCCell + cell.setModel(item) return cell } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return 6 + return items.count } } extension AwardListView:UICollectionViewDelegateFlowLayout{ func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - let w = (self.jq_width - 74 - 190) / 3.0 + let w = (JQ_ScreenW - 37 * 2 - 144 * 2 - 44 * 2) / 3.0 return CGSize(width: w, height: w * 1.552) } diff --git a/DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketCCell.swift b/DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketCCell.swift index 0a4e206..cb6e8e5 100644 --- a/DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketCCell.swift +++ b/DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketCCell.swift @@ -18,6 +18,16 @@ } + var marketModel:MarketModel?{ + didSet{ + if let m = marketModel{ + cover_imageView.sd_setImage(with: URL(string: m.coverImg)) + label_title.text = m.name + label_coin.text = "\(m.integral)积分" + } + } + } + override func layoutSubviews() { super.layoutSubviews() jq_addShadows(shadowColor: UIColor(hexStr: "#D9D9D9"), corner: 8, radius: 20, offset: CGSize(width: 0, height: 20), opacity: 1) diff --git a/DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketTagCCell.swift b/DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketTagCCell.swift new file mode 100644 index 0000000..0180dc4 --- /dev/null +++ b/DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketTagCCell.swift @@ -0,0 +1,20 @@ +// +// MarketTagCCell.swift +// DolphinEnglishLearnStudent +// +// Created by 无故事王国 on 2024/6/3. +// + +import UIKit + +class MarketTagCCell: UICollectionViewCell { + + @IBOutlet weak var view_container: UIView! + @IBOutlet weak var label_name: UILabel! + + override func awakeFromNib() { + super.awakeFromNib() + view_container.jq_borderColor = UIColor(hexString: "#41A2EB") + } + +} diff --git a/DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketTagCCell.xib b/DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketTagCCell.xib new file mode 100644 index 0000000..b8982d1 --- /dev/null +++ b/DolphinEnglishLearnStudent/Moudle/Market/CCell/MarketTagCCell.xib @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> + <device id="ipad10_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/> + <capability name="System colors in document resources" minToolsVersion="11.0"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <objects> + <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> + <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> + <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="_MarketTagCCell" id="gTV-IL-0wX" customClass="MarketTagCCell" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="106" height="66"/> + <autoresizingMask key="autoresizingMask"/> + <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center"> + <rect key="frame" x="0.0" y="0.0" width="106" height="66"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <subviews> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ynz-c6-wvK"> + <rect key="frame" x="0.0" y="0.0" width="106" height="66"/> + <subviews> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fam-fP-fIA"> + <rect key="frame" x="17" y="10" width="72" height="47"/> + <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/> + <color key="textColor" red="0.25490196079999999" green="0.63529411759999999" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + </subviews> + <color key="backgroundColor" systemColor="systemBackgroundColor"/> + <constraints> + <constraint firstAttribute="trailing" secondItem="fam-fP-fIA" secondAttribute="trailing" constant="17" id="Zzp-0t-bAM"/> + <constraint firstAttribute="bottom" secondItem="fam-fP-fIA" secondAttribute="bottom" constant="9" id="aCk-9L-SKS"/> + <constraint firstItem="fam-fP-fIA" firstAttribute="top" secondItem="ynz-c6-wvK" secondAttribute="top" constant="10" id="k72-Fv-BOv"/> + <constraint firstItem="fam-fP-fIA" firstAttribute="leading" secondItem="ynz-c6-wvK" secondAttribute="leading" constant="17" id="wC7-gL-7u1"/> + </constraints> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="boolean" keyPath="ld_maskToBoundsXIB" value="YES"/> + <userDefinedRuntimeAttribute type="number" keyPath="ld_cornerRadiusXIB"> + <real key="value" value="20.5"/> + </userDefinedRuntimeAttribute> + </userDefinedRuntimeAttributes> + </view> + </subviews> + </view> + <constraints> + <constraint firstAttribute="trailing" secondItem="ynz-c6-wvK" secondAttribute="trailing" id="QjW-oE-Tgj"/> + <constraint firstAttribute="bottom" secondItem="ynz-c6-wvK" secondAttribute="bottom" id="e07-4t-A2U"/> + <constraint firstItem="ynz-c6-wvK" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="ew2-Uv-ect"/> + <constraint firstItem="ynz-c6-wvK" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="ohm-Eq-BKS"/> + </constraints> + <size key="customSize" width="106" height="66"/> + <connections> + <outlet property="label_name" destination="fam-fP-fIA" id="uca-7s-UDX"/> + <outlet property="view_container" destination="ynz-c6-wvK" id="05W-u9-fnI"/> + </connections> + <point key="canvasLocation" x="68.048780487804876" y="23.898305084745765"/> + </collectionViewCell> + </objects> + <resources> + <systemColor name="systemBackgroundColor"> + <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </systemColor> + </resources> +</document> diff --git a/DolphinEnglishLearnStudent/Moudle/Market/MarketVC.swift b/DolphinEnglishLearnStudent/Moudle/Market/MarketVC.swift index 47e263e..0e4998a 100644 --- a/DolphinEnglishLearnStudent/Moudle/Market/MarketVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Market/MarketVC.swift @@ -7,17 +7,51 @@ import UIKit import QMUIKit +import RxRelay +import RxSwift + +class MarketListViewModel:RefreshInnerModel<MarketModel>{ + + let keywords = BehaviorRelay<String>(value:"") + let types = BehaviorRelay<[String]>(value:[]) + let menuTypes = BehaviorRelay<[MarketTypeModel]>(value:[]) + var selectMenuTypes = Set<MarketTypeModel>(){ + didSet{ + types.accept(selectMenuTypes.map({$0.name})) + beginRefresh() + } + } + + override func api() -> (Observable<BaseResponse<BaseResponseList<MarketModel>>>)? { + Services.goodsList(keywords: keywords.value, page: page, type: types.value) + } +} + class MarketVC: BaseVC { @IBOutlet weak var tf_search: QMUITextField! @IBOutlet weak var menu_collectView: UICollectionView! @IBOutlet weak var content_collectionView: UICollectionView! + @IBOutlet weak var label_surplusCoin: UILabel! + + private let viewModel = MarketListViewModel() private var cellW = (JQ_ScreenW - 130 - 15) / 4.0 override func viewDidLoad() { super.viewDidLoad() + viewModel.configure(content_collectionView,needMore: true) + viewModel.beginRefresh() + + Services.getIntegral().subscribe(onNext: {[weak self] result in + self?.label_surplusCoin.text = "\(result.data ?? 0)" + }).disposed(by: disposeBag) + + Services.goodTypeStudy().subscribe(onNext: {[weak self] result in + self?.viewModel.menuTypes.accept(result.data ?? []) + self?.menu_collectView.reloadData() + }).disposed(by: disposeBag) } override func setUI() { @@ -28,39 +62,93 @@ content_collectionView.backgroundColor = .clear content_collectionView.contentInset = UIEdgeInsets(top: 0, left: 65, bottom: 0, right:65) content_collectionView.register(UINib(nibName: "MarketCCell", bundle: nil), forCellWithReuseIdentifier: "_MarketCCell") + tf_search.returnKeyType = .search + tf_search.delegate = self + + menu_collectView.backgroundColor = .clear + menu_collectView.dataSource = self + menu_collectView.delegate = self + menu_collectView.register(UINib(nibName: "MarketTagCCell", bundle: nil), forCellWithReuseIdentifier: "_MarketTagCCell") } @IBAction func searchAction(_ sender: UIButton) { - + view.endEditing(true) + viewModel.keywords.accept(tf_search.text!) + viewModel.beginRefresh() } } extension MarketVC:UICollectionViewDelegate{ func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - let vc = MarketContentVC() - vc.title = "商品详情" - push(vc: vc) + + if collectionView == menu_collectView{ + let model = viewModel.menuTypes.value[indexPath.row] + if viewModel.selectMenuTypes.contains(model){ + viewModel.selectMenuTypes.remove(model) + }else{ + viewModel.selectMenuTypes.insert(model) + } + collectionView.reloadData() + }else{ + let v = viewModel.dataSource.value?.records[indexPath.row] + let vc = MarketContentVC(goodsId: v?.id ?? 0) + vc.title = "商品详情" + push(vc: vc) + } } } extension MarketVC:UICollectionViewDataSource{ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return 30 + + if collectionView == menu_collectView{ + return viewModel.menuTypes.value.count + } + + return viewModel.dataSource.value?.records.count ?? 0 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + + if collectionView == menu_collectView{ + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_MarketTagCCell", for: indexPath) as! MarketTagCCell + let model = viewModel.menuTypes.value[indexPath.row] + cell.label_name.text = model.name + + if viewModel.selectMenuTypes.contains(model){ + cell.view_container.backgroundColor = UIColor(hexString: "#41A2EB") + cell.view_container.jq_borderWidth = 0 + cell.label_name.textColor = .white + }else{ + cell.view_container.backgroundColor = UIColor.white + cell.view_container.jq_borderWidth = 1 + cell.label_name.textColor = UIColor(hexString: "#41A2EB") + } + return cell + } + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_MarketCCell", for: indexPath) as! MarketCCell - cell.backgroundColor = .gray.withAlphaComponent(0.5) + let model = viewModel.dataSource.value?.records[indexPath.row] + cell.marketModel = model + cell.backgroundColor = .white return cell } } extension MarketVC:UICollectionViewDelegateFlowLayout{ func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + + if collectionView == menu_collectView{ + return CGSize(width: 100, height: 41) + } + return CGSize(width: cellW, height: cellW * 1.09) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + if collectionView == menu_collectView{ + return 29 + } return 24 } @@ -68,3 +156,12 @@ return 5 } } + +extension MarketVC:QMUITextFieldDelegate{ + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + view.endEditing(true) + viewModel.keywords.accept(textField.text!) + viewModel.beginRefresh() + return true + } +} diff --git a/DolphinEnglishLearnStudent/Moudle/Market/MarketVC.xib b/DolphinEnglishLearnStudent/Moudle/Market/MarketVC.xib index c2ad621..dd20976 100644 --- a/DolphinEnglishLearnStudent/Moudle/Market/MarketVC.xib +++ b/DolphinEnglishLearnStudent/Moudle/Market/MarketVC.xib @@ -12,6 +12,7 @@ <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MarketVC" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> <connections> <outlet property="content_collectionView" destination="EPp-Vf-S4a" id="nYe-TY-YF6"/> + <outlet property="label_surplusCoin" destination="82B-Dg-Sap" id="oRO-iM-jgF"/> <outlet property="menu_collectView" destination="OCn-Jg-gWg" id="v5E-bg-k90"/> <outlet property="tf_search" destination="Jv9-DX-cX8" id="Y6V-ux-8OL"/> <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> diff --git a/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketContentVC.swift b/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketContentVC.swift index e5ef208..5daa03a 100644 --- a/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketContentVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketContentVC.swift @@ -7,11 +7,24 @@ import UIKit import WebKit +import RxRelay + +class MarketContentViewModel{ + //购买数量 + var number = BehaviorRelay<Int>(value:1) + var detailModel = BehaviorRelay<MarketDetailModel?>(value:nil) + var surplusCoin = BehaviorRelay<Int>(value:0) + var goodsId = BehaviorRelay<Int>(value:0) + +// var orderNumber = BehaviorRelay<String>(value:"") + //收获地址 + var address = BehaviorRelay<[AddressModel]>(value: []) +} class MarketContentVC: BaseVC { @IBOutlet weak var scrollView: UIScrollView! - @IBOutlet weak var image_cover: UIImageView! + @IBOutlet weak var view_banner: CommonBannerView! @IBOutlet weak var view_footer: UIView! @IBOutlet weak var view_container: UIView! @IBOutlet weak var label_coin: UILabel! @@ -22,10 +35,52 @@ @IBOutlet weak var label_surplusCoin: UILabel! @IBOutlet weak var label_costCoin: UILabel! @IBOutlet weak var cons_footHei: NSLayoutConstraint! + @IBOutlet weak var webViewHeiCons: NSLayoutConstraint! + + private var viewModel = MarketContentViewModel() + + required init(goodsId:Int) { + super.init(nibName: nil, bundle: nil) + self.viewModel.goodsId.accept(goodsId) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } override func viewDidLoad() { super.viewDidLoad() + Services.goodsDetail(goodsId: viewModel.goodsId.value).subscribe(onNext: {[weak self] result in + if let m = result.data{ + + let banner = m.good?.detailImg.components(separatedBy: ",").map({ url in + return CommonBannerModel(index: 0, id: 0, name: nil, resource: url, mediaType: .imageUrl) + }) + + self?.view_banner.setItems(items: banner ?? [], autoRoll: true, selectClouse: nil) + + self?.label_coin.text = "\(m.good?.integral ?? 0)" + self?.label_costCoin.text = "\(m.good?.integral ?? 0)" + self?.label_title.text = m.good?.name ?? "" + self?.label_categry.text = m.goodTypes.map({$0.name}).joined(separator: "|") + + var info_Array = Array<String>() + info_Array.append("剩余数量:\(m.good?.surplus ?? 0)") + info_Array.append("可换数量:\(m.good?.userCount ?? 0)") + info_Array.append("\(m.exchangeNumber)人兑换") + + self?.label_info.text = info_Array.joined(separator: "|") + self?.webView.loadHTMLString(m.good?.detail.jq_wrapHtml() ?? "", baseURL: nil) + self?.viewModel.detailModel.accept(m) + } + + }).disposed(by: disposeBag) + + Services.getIntegral().subscribe(onNext: {[weak self] result in + self?.label_surplusCoin.text = "剩余积分:\(result.data ?? 0)" + self?.viewModel.surplusCoin.accept(result.data ?? 0) + }).disposed(by: disposeBag) } override func setUI() { @@ -34,20 +89,51 @@ cons_footHei.constant = 54 + UIDevice.jq_safeEdges.bottom } + override func setRx() { + webView.scrollView.rx.observe(CGSize.self, "contentSize").map { (size) -> CGFloat? in + if let size = size{ + return size.height + } + return nil + }.subscribe(onNext: { [unowned self](height) in + if let height = height{ + self.webViewHeiCons.constant = height + } + }).disposed(by: disposeBag) + } + @IBAction func exchangeAction(_ sender: UIButton) { -// CommonAlertView.show(isSinple: true, content: "兑换失败,当前剩余积分不足!") { -// -// } - let vc = MarketExchangeVC() + guard viewModel.detailModel.value?.good?.surplus != 0 else { + CommonAlertView.show(isSinple: true, content: "兑换失败,当前剩余数量不足!") { + + } + return + } + + guard viewModel.detailModel.value?.good?.userCount != 0 else { + CommonAlertView.show(isSinple: true, content: "兑换失败,当前可换数量不足!") { + + } + return + } + + guard (viewModel.detailModel.value?.good?.integral ?? 0) < viewModel.surplusCoin.value else { + CommonAlertView.show(isSinple: true, content: "兑换失败,当前剩余积分不足!") { + + } + return + } + + let vc = MarketExchangeVC(viewModel: viewModel) vc.title = "立即兑换" push(vc: vc) } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - image_cover.jq_addCorners(corner: [.topLeft,.topRight], radius: 8) + view_banner.jq_addCorners(corner: [.topLeft,.topRight], radius: 8) view_footer.jq_addShadows(shadowColor: UIColor(hexStr: "#DEDEDE").withAlphaComponent(0.5), corner: 0, radius: 4, offset: CGSize(width: 0, height: -1), opacity: 1) view_container.jq_addShadows(shadowColor: UIColor(hexStr: "#D9D9D9").withAlphaComponent(0.28), corner: 8, radius: 5, offset: CGSize(width: 0, height: 2), opacity: 1) diff --git a/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketContentVC.xib b/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketContentVC.xib index 1e2ccf3..fc87ec6 100644 --- a/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketContentVC.xib +++ b/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketContentVC.xib @@ -12,7 +12,6 @@ <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MarketContentVC" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> <connections> <outlet property="cons_footHei" destination="O1c-0y-Iw9" id="Oj2-S6-uVD"/> - <outlet property="image_cover" destination="HQ8-qZ-zeA" id="0xr-UI-5I9"/> <outlet property="label_categry" destination="YLo-dD-PTM" id="kCG-k8-Baa"/> <outlet property="label_coin" destination="yrU-ab-vv9" id="4UT-Q9-CO3"/> <outlet property="label_costCoin" destination="Br9-rb-zWu" id="Qyp-x1-Ff0"/> @@ -21,9 +20,11 @@ <outlet property="label_title" destination="PgE-zX-EIu" id="FWi-qa-sK0"/> <outlet property="scrollView" destination="loc-rm-BZe" id="xqU-OW-GgJ"/> <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> + <outlet property="view_banner" destination="lUx-IV-YIU" id="9RA-3Q-ceF"/> <outlet property="view_container" destination="3V0-GL-Fmn" id="D6J-fr-4H2"/> <outlet property="view_footer" destination="mp9-Bg-Kez" id="41u-8O-co7"/> <outlet property="webView" destination="ZOY-ws-sjP" id="bXL-3r-nKs"/> + <outlet property="webViewHeiCons" destination="xMb-sR-Ext" id="8RD-Be-b0n"/> </connections> </placeholder> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> @@ -93,13 +94,6 @@ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3V0-GL-Fmn"> <rect key="frame" x="0.0" y="0.0" width="556" height="681"/> <subviews> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="HQ8-qZ-zeA"> - <rect key="frame" x="0.0" y="0.0" width="556" height="319.5"/> - <color key="backgroundColor" systemColor="systemGray5Color"/> - <constraints> - <constraint firstAttribute="width" secondItem="HQ8-qZ-zeA" secondAttribute="height" multiplier="1:0.575" id="4sO-41-6AB"/> - </constraints> - </imageView> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yrU-ab-vv9"> <rect key="frame" x="14" y="329.5" width="10" height="25"/> <constraints> @@ -171,30 +165,37 @@ <wkPreferences key="preferences"/> </wkWebViewConfiguration> </wkWebView> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lUx-IV-YIU" customClass="CommonBannerView" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="556" height="319.5"/> + <color key="backgroundColor" systemColor="systemBackgroundColor"/> + <constraints> + <constraint firstAttribute="width" secondItem="lUx-IV-YIU" secondAttribute="height" multiplier="1:0.575" id="Vfi-RM-kqs"/> + </constraints> + </view> </subviews> <color key="backgroundColor" systemColor="systemBackgroundColor"/> <constraints> + <constraint firstItem="yrU-ab-vv9" firstAttribute="top" secondItem="lUx-IV-YIU" secondAttribute="bottom" constant="10" id="0Z9-Wg-moE"/> <constraint firstItem="fkp-fX-eUo" firstAttribute="top" secondItem="3WZ-Jf-leh" secondAttribute="bottom" constant="15" id="1lu-mH-O3t"/> - <constraint firstItem="HQ8-qZ-zeA" firstAttribute="leading" secondItem="3V0-GL-Fmn" secondAttribute="leading" id="2L8-TY-eC1"/> <constraint firstAttribute="trailing" secondItem="SHM-HW-3QJ" secondAttribute="trailing" constant="14" id="3fT-Df-Icu"/> <constraint firstItem="ZOY-ws-sjP" firstAttribute="top" secondItem="fkp-fX-eUo" secondAttribute="bottom" constant="10" id="4Sb-GV-SMH"/> <constraint firstItem="fkp-fX-eUo" firstAttribute="leading" secondItem="3V0-GL-Fmn" secondAttribute="leading" constant="14" id="CGr-W8-bYc"/> + <constraint firstItem="lUx-IV-YIU" firstAttribute="top" secondItem="3V0-GL-Fmn" secondAttribute="top" id="CJV-El-ct7"/> <constraint firstItem="LtS-Mg-aeT" firstAttribute="centerY" secondItem="yrU-ab-vv9" secondAttribute="centerY" id="Eqy-fC-VXN"/> <constraint firstItem="SHM-HW-3QJ" firstAttribute="leading" secondItem="3V0-GL-Fmn" secondAttribute="leading" constant="14" id="IcX-sV-UsY"/> <constraint firstAttribute="trailing" secondItem="PgE-zX-EIu" secondAttribute="trailing" constant="14" id="KOH-43-2e0"/> + <constraint firstItem="lUx-IV-YIU" firstAttribute="leading" secondItem="3V0-GL-Fmn" secondAttribute="leading" id="OoB-ef-zF5"/> <constraint firstItem="3WZ-Jf-leh" firstAttribute="top" secondItem="SHM-HW-3QJ" secondAttribute="bottom" constant="15" id="TZb-uR-n12"/> <constraint firstItem="yrU-ab-vv9" firstAttribute="leading" secondItem="3V0-GL-Fmn" secondAttribute="leading" constant="14" id="bvf-qv-PXf"/> <constraint firstItem="LtS-Mg-aeT" firstAttribute="leading" secondItem="yrU-ab-vv9" secondAttribute="trailing" id="ce9-HT-eYR"/> - <constraint firstItem="yrU-ab-vv9" firstAttribute="top" secondItem="HQ8-qZ-zeA" secondAttribute="bottom" constant="10" id="fcu-Lq-OG7"/> <constraint firstAttribute="trailing" secondItem="3WZ-Jf-leh" secondAttribute="trailing" constant="13" id="gDs-iU-l4D"/> - <constraint firstItem="HQ8-qZ-zeA" firstAttribute="top" secondItem="3V0-GL-Fmn" secondAttribute="top" id="gge-7H-yMR"/> + <constraint firstAttribute="trailing" secondItem="lUx-IV-YIU" secondAttribute="trailing" id="i5N-By-Pa9"/> <constraint firstItem="ZOY-ws-sjP" firstAttribute="leading" secondItem="3V0-GL-Fmn" secondAttribute="leading" constant="14" id="k11-lO-F1N"/> <constraint firstItem="SHM-HW-3QJ" firstAttribute="top" secondItem="PgE-zX-EIu" secondAttribute="bottom" constant="7" id="kjs-SE-QJz"/> <constraint firstItem="3WZ-Jf-leh" firstAttribute="leading" secondItem="3V0-GL-Fmn" secondAttribute="leading" constant="13" id="msq-Yj-Hkl"/> <constraint firstItem="PgE-zX-EIu" firstAttribute="top" secondItem="yrU-ab-vv9" secondAttribute="bottom" constant="7" id="tro-c3-NxF"/> <constraint firstItem="PgE-zX-EIu" firstAttribute="leading" secondItem="3V0-GL-Fmn" secondAttribute="leading" constant="14" id="upN-6F-Xy0"/> <constraint firstAttribute="bottom" secondItem="ZOY-ws-sjP" secondAttribute="bottom" constant="10" id="x2O-1I-fif"/> - <constraint firstAttribute="trailing" secondItem="HQ8-qZ-zeA" secondAttribute="trailing" id="xc1-2D-IN1"/> <constraint firstAttribute="trailing" secondItem="ZOY-ws-sjP" secondAttribute="trailing" constant="14" id="zdu-6P-uIG"/> </constraints> </view> @@ -225,9 +226,6 @@ <resources> <systemColor name="systemBackgroundColor"> <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </systemColor> - <systemColor name="systemGray5Color"> - <color red="0.89803921568627454" green="0.89803921568627454" blue="0.91764705882352937" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </systemColor> </resources> </document> diff --git a/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketExchangeVC.swift b/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketExchangeVC.swift index 9cad328..02bb8c4 100644 --- a/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketExchangeVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketExchangeVC.swift @@ -7,10 +7,7 @@ import UIKit import RxRelay - -class MarketViewModel{ - var number = BehaviorRelay<Int>(value:1) -} +import QMUIKit class MarketExchangeVC: BaseVC { @IBOutlet weak var scrollView: UIScrollView! @@ -18,11 +15,57 @@ @IBOutlet weak var view_container: UIView! @IBOutlet weak var field_number: UITextField! @IBOutlet weak var cons_footerHei: NSLayoutConstraint! + @IBOutlet weak var label_address: UILabel! + @IBOutlet weak var label_address_info: UILabel! + @IBOutlet weak var img_cover: UIImageView! + @IBOutlet weak var label_goodsName: UILabel! + @IBOutlet weak var label_coin: UILabel! + @IBOutlet weak var btn_add: UIButton! + @IBOutlet weak var btn_reduce: UIButton! + @IBOutlet weak var textView_remark: QMUITextView! + @IBOutlet weak var label_num: UILabel! + @IBOutlet weak var label_needCoin: UILabel! + @IBOutlet weak var label_orderNum: UILabel! + @IBOutlet weak var label_footNeedCoin: UILabel! - private var viewModel = MarketViewModel() + private var viewModel = MarketContentViewModel() + required init(viewModel:MarketContentViewModel) { + super.init(nibName: nil, bundle: nil) + self.viewModel = viewModel + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() + + Services.redeemNow(goodId: viewModel.detailModel.value!.good!.id).subscribe(onNext: {result in + self.label_orderNum.text = result.data?.orderNumber ?? "" + self.viewModel.detailModel.accept(result.data) + }).disposed(by: disposeBag) + + Services.addressList().subscribe(onNext: {[weak self]result in + self?.viewModel.address.accept(result.data ?? []) + if let first = result.data?.filter({$0.isDefault == 1}).first{ + self?.label_address.text = first.address + self?.label_address_info.text = first.recipient + "|" + first.recipientPhone + }else{ + self?.label_address.text = "新建收货地址" + self?.label_address_info.isHidden = true + } + }).disposed(by: disposeBag) + + self.img_cover.sd_setImage(with: URL(string: viewModel.detailModel.value?.good?.coverImg)) + self.label_goodsName.text = viewModel.detailModel.value?.good?.name ?? "" + self.label_coin.text = "\(viewModel.detailModel.value?.good?.integral ?? 0)" + if viewModel.detailModel.value?.good?.userCount == 1{ + btn_add.isEnabled = false + btn_reduce.isEnabled = false + field_number.isEnabled = false + } } override func setUI() { @@ -35,25 +78,62 @@ override func setRx() { viewModel.number.subscribe(onNext: {[weak self] num in self?.field_number.text = "\(num)" + self?.label_num.text = "\(num)" + let totalCoin = num * (self?.viewModel.detailModel.value?.good?.integral ?? 0) + self?.label_needCoin.text = "\(totalCoin)" + self?.label_footNeedCoin.text = "\(totalCoin)" }).disposed(by: disposeBag) } - + @IBAction func addressAction(_ sender: Any) { + if viewModel.address.value.filter({$0.isDefault == 1}).count > 0{ + let vc = AddressManageVC(type: .choose) + vc.title = "地址管理" + push(vc: vc) + }else{ + let vc = AddressManageVC(type: .handle) + vc.title = "地址管理" + push(vc: vc) + } + } + @IBAction func addNumAction(_ sender: UIButton) { - let num = viewModel.number.value + 1 + var num = viewModel.number.value + 1 + if num >= viewModel.detailModel.value?.good?.userCount ?? 0{ + num = viewModel.detailModel.value?.good?.userCount ?? 0 + sender.isEnabled = false + } viewModel.number.accept(num) } @IBAction func reduceAction(_ sender: UIButton) { let num = max(1,viewModel.number.value - 1) viewModel.number.accept(num) + btn_add.isEnabled = true } @IBAction func exchangeAction(_ sender: UIButton) { + + guard viewModel.address.value.filter({$0.isDefault == 1}).count != 0 else{ + CommonAlertView.show(content: "请先设置收货地址") { + let vc = AddressManageVC(type: .handle) + vc.title = "地址管理" + self.push(vc: vc) + } + return + } + CommonAlertView.show(content: "确认兑换当前商品吗?") { - let vc = ExchangeResultVC(resultType: .success) - vc.title = "商品详情" - self.push(vc: vc) + let goodsId = self.viewModel.detailModel.value!.good!.id + let num = self.viewModel.number.value + let orderNumber = self.viewModel.detailModel.value?.orderNumber ?? "" + let recipientId = self.viewModel.detailModel.value?.recipient?.id ?? 0 + + Services.goodsExchangeStudy(goodsId: goodsId, number: num, orderNumber: orderNumber, recipientId: recipientId, remark: self.textView_remark.text!).subscribe(onNext: {_ in + let vc = ExchangeResultVC(resultType: .success) + vc.title = "商品详情" + self.push(vc: vc) + }).disposed(by: self.disposeBag) } } } diff --git a/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketExchangeVC.xib b/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketExchangeVC.xib index 10ddf4e..7835f2e 100644 --- a/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketExchangeVC.xib +++ b/DolphinEnglishLearnStudent/Moudle/Market/VC/MarketExchangeVC.xib @@ -11,9 +11,21 @@ <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MarketExchangeVC" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> <connections> + <outlet property="btn_add" destination="wqg-IO-Jti" id="tqa-wd-rR2"/> + <outlet property="btn_reduce" destination="H9G-ck-ezM" id="uPJ-2M-QEo"/> <outlet property="cons_footerHei" destination="71z-Eh-Ya8" id="V0f-Rh-ggi"/> <outlet property="field_number" destination="1nG-XY-fwB" id="xye-yR-VBc"/> + <outlet property="img_cover" destination="QM5-dP-No2" id="xux-xq-ol5"/> + <outlet property="label_address" destination="ph9-Ra-CxP" id="b0j-bK-UQV"/> + <outlet property="label_address_info" destination="Hns-QV-baw" id="v5s-wf-nHN"/> + <outlet property="label_coin" destination="Zy6-3Q-Lgi" id="WNc-y4-9Yn"/> + <outlet property="label_footNeedCoin" destination="Nka-ZK-eeP" id="1wN-Kd-xoF"/> + <outlet property="label_goodsName" destination="SZF-J0-byW" id="bMb-RR-rHM"/> + <outlet property="label_needCoin" destination="vFF-je-HEk" id="XDA-D5-HO1"/> + <outlet property="label_num" destination="p4X-ju-9r7" id="Cmx-HF-NKT"/> + <outlet property="label_orderNum" destination="BaU-rt-9wh" id="fwg-Lq-bZB"/> <outlet property="scrollView" destination="hFz-gY-osL" id="xXh-5z-6XE"/> + <outlet property="textView_remark" destination="Rq2-vc-D0Q" id="sDq-vQ-gM6"/> <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> <outlet property="view_container" destination="4wG-oJ-eBG" id="8U4-Q9-4Ap"/> <outlet property="view_footer" destination="d3o-Ba-FIK" id="EMz-WT-ojz"/> @@ -80,7 +92,7 @@ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="HCf-Ym-o1O"> <rect key="frame" x="0.0" y="0.0" width="556" height="600"/> <subviews> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sfn-3Q-b7V"> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sfn-3Q-b7V" customClass="TapBtn" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="556" height="90"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5h7-8X-cXI"> @@ -90,44 +102,48 @@ <constraint firstAttribute="height" constant="3" id="7Bs-Y1-RKJ"/> </constraints> </view> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ph9-Ra-CxP"> - <rect key="frame" x="14" y="28" width="492" height="20"/> - <constraints> - <constraint firstAttribute="height" constant="20" id="cgw-w2-dsN"/> - </constraints> - <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/> - <color key="textColor" red="0.011764705882352941" green="0.015686274509803921" blue="0.019607843137254902" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <nil key="highlightedColor"/> - </label> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_more_gray" translatesAutoresizingMaskIntoConstraints="NO" id="jKh-8m-ogE"> <rect key="frame" x="536" y="40" width="6" height="10"/> </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Hns-QV-baw"> - <rect key="frame" x="14" y="53" width="492" height="14.5"/> - <fontDescription key="fontDescription" type="system" pointSize="12"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> + <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="ZAd-Ha-PgP"> + <rect key="frame" x="14" y="25.5" width="36" height="39.5"/> + <subviews> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ph9-Ra-CxP"> + <rect key="frame" x="0.0" y="0.0" width="36" height="20"/> + <constraints> + <constraint firstAttribute="height" constant="20" id="cgw-w2-dsN"/> + </constraints> + <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/> + <color key="textColor" red="0.011764705882352941" green="0.015686274509803921" blue="0.019607843137254902" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Hns-QV-baw"> + <rect key="frame" x="0.0" y="25" width="36" height="14.5"/> + <fontDescription key="fontDescription" type="system" pointSize="12"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + </subviews> + </stackView> </subviews> <constraints> <constraint firstItem="jKh-8m-ogE" firstAttribute="centerY" secondItem="sfn-3Q-b7V" secondAttribute="centerY" id="AIT-eX-UYg"/> <constraint firstAttribute="trailing" secondItem="5h7-8X-cXI" secondAttribute="trailing" id="Byx-At-ZHn"/> <constraint firstAttribute="height" constant="90" id="Gtx-jC-H0V"/> - <constraint firstItem="ph9-Ra-CxP" firstAttribute="leading" secondItem="sfn-3Q-b7V" secondAttribute="leading" constant="14" id="L6f-uW-gIs"/> - <constraint firstItem="ph9-Ra-CxP" firstAttribute="top" secondItem="sfn-3Q-b7V" secondAttribute="top" constant="28" id="Pzt-wT-2Uz"/> + <constraint firstItem="ZAd-Ha-PgP" firstAttribute="leading" secondItem="sfn-3Q-b7V" secondAttribute="leading" constant="14" id="KKV-Kz-UZ6"/> <constraint firstAttribute="bottom" secondItem="5h7-8X-cXI" secondAttribute="bottom" id="dS7-Gn-QOD"/> - <constraint firstAttribute="trailing" secondItem="jKh-8m-ogE" secondAttribute="trailing" constant="14" id="fpD-Vw-mh2"/> <constraint firstItem="5h7-8X-cXI" firstAttribute="leading" secondItem="sfn-3Q-b7V" secondAttribute="leading" id="g86-ED-gAW"/> - <constraint firstItem="Hns-QV-baw" firstAttribute="trailing" secondItem="ph9-Ra-CxP" secondAttribute="trailing" id="hFX-F5-1NJ"/> - <constraint firstItem="Hns-QV-baw" firstAttribute="leading" secondItem="ph9-Ra-CxP" secondAttribute="leading" id="l9W-26-0ij"/> - <constraint firstItem="Hns-QV-baw" firstAttribute="top" secondItem="ph9-Ra-CxP" secondAttribute="bottom" constant="5" id="mta-MA-VXe"/> - <constraint firstAttribute="trailing" secondItem="ph9-Ra-CxP" secondAttribute="trailing" constant="50" id="oe6-gk-huK"/> + <constraint firstAttribute="trailing" secondItem="jKh-8m-ogE" secondAttribute="trailing" constant="14" id="on7-ez-QYT"/> + <constraint firstItem="ZAd-Ha-PgP" firstAttribute="centerY" secondItem="sfn-3Q-b7V" secondAttribute="centerY" id="qhc-bm-AqF"/> </constraints> + <connections> + <action selector="addressAction:" destination="-1" eventType="touchUpInside" id="fj4-mV-Wor"/> + </connections> </view> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Pcy-2J-bsd"> <rect key="frame" x="0.0" y="90" width="556" height="366"/> <subviews> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="QM5-dP-No2"> + <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="QM5-dP-No2"> <rect key="frame" x="14" y="15" width="170" height="170"/> <constraints> <constraint firstAttribute="width" constant="170" id="gTg-aH-0dy"/> @@ -239,9 +255,8 @@ <action selector="addNumAction:" destination="-1" eventType="touchUpInside" id="N9f-5O-JZz"/> </connections> </button> - <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="H9G-ck-ezM"> - <rect key="frame" x="702" y="206" width="32" height="22"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="H9G-ck-ezM"> + <rect key="frame" x="440" y="207" width="32" height="22"/> <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> <state key="normal" image="btn_reduce"/> <state key="disabled" image="btn_reduce_un"/> @@ -253,11 +268,13 @@ <color key="backgroundColor" systemColor="systemBackgroundColor"/> <constraints> <constraint firstAttribute="trailing" secondItem="SZF-J0-byW" secondAttribute="trailing" constant="13" id="2bc-jD-0jQ"/> + <constraint firstItem="Y1j-mH-7vJ" firstAttribute="centerY" secondItem="H9G-ck-ezM" secondAttribute="centerY" id="3fZ-cn-vBb"/> <constraint firstItem="QM5-dP-No2" firstAttribute="top" secondItem="Pcy-2J-bsd" secondAttribute="top" constant="15" id="3oV-je-E0W"/> <constraint firstItem="SZF-J0-byW" firstAttribute="leading" secondItem="QM5-dP-No2" secondAttribute="trailing" constant="13" id="4nD-DW-K4G"/> <constraint firstItem="wqg-IO-Jti" firstAttribute="leading" secondItem="Y1j-mH-7vJ" secondAttribute="trailing" id="96J-NH-U7L"/> <constraint firstItem="MS7-3T-DWG" firstAttribute="top" secondItem="EeU-Wx-jBI" secondAttribute="bottom" constant="17" id="APQ-K4-sOj"/> <constraint firstAttribute="trailing" secondItem="xEk-ac-udO" secondAttribute="trailing" constant="14" id="AaJ-eg-xRI"/> + <constraint firstItem="wqg-IO-Jti" firstAttribute="height" secondItem="H9G-ck-ezM" secondAttribute="height" id="BxV-pF-PDX"/> <constraint firstAttribute="trailing" secondItem="Y1j-mH-7vJ" secondAttribute="trailing" constant="42" id="Cnz-nI-ucx"/> <constraint firstItem="xEk-ac-udO" firstAttribute="leading" secondItem="Pcy-2J-bsd" secondAttribute="leading" constant="12" id="DSj-62-NMj"/> <constraint firstItem="MS7-3T-DWG" firstAttribute="leading" secondItem="EeU-Wx-jBI" secondAttribute="leading" id="EM3-jV-CSR"/> @@ -269,6 +286,7 @@ <constraint firstItem="Y1j-mH-7vJ" firstAttribute="top" secondItem="h4B-Ju-L2P" secondAttribute="bottom" constant="22" id="RcG-f4-yEi"/> <constraint firstItem="h4B-Ju-L2P" firstAttribute="centerY" secondItem="Zy6-3Q-Lgi" secondAttribute="centerY" id="ScA-Kv-FAY"/> <constraint firstItem="xEk-ac-udO" firstAttribute="top" secondItem="5zn-TC-ZAu" secondAttribute="bottom" constant="14" id="TRf-HU-Y0g"/> + <constraint firstItem="wqg-IO-Jti" firstAttribute="width" secondItem="H9G-ck-ezM" secondAttribute="width" id="TaG-sx-WqG"/> <constraint firstAttribute="trailing" secondItem="5zn-TC-ZAu" secondAttribute="trailing" constant="14" id="XsR-S7-5se"/> <constraint firstItem="h4B-Ju-L2P" firstAttribute="leading" secondItem="Zy6-3Q-Lgi" secondAttribute="trailing" id="XyT-7a-Pzk"/> <constraint firstItem="5zn-TC-ZAu" firstAttribute="top" secondItem="MS7-3T-DWG" secondAttribute="bottom" constant="12" id="Zb6-DZ-qrC"/> @@ -276,6 +294,7 @@ <constraint firstItem="EeU-Wx-jBI" firstAttribute="top" secondItem="QM5-dP-No2" secondAttribute="bottom" constant="18" id="kWP-Z3-Uve"/> <constraint firstItem="QM5-dP-No2" firstAttribute="leading" secondItem="Pcy-2J-bsd" secondAttribute="leading" constant="14" id="lJI-a1-yyL"/> <constraint firstItem="EeU-Wx-jBI" firstAttribute="leading" secondItem="Pcy-2J-bsd" secondAttribute="leading" constant="14" id="pIg-OO-tpF"/> + <constraint firstItem="Y1j-mH-7vJ" firstAttribute="leading" secondItem="H9G-ck-ezM" secondAttribute="trailing" id="v1W-TC-i1N"/> <constraint firstItem="h4B-Ju-L2P" firstAttribute="bottom" secondItem="QM5-dP-No2" secondAttribute="bottom" id="z2m-b7-4WH"/> </constraints> </view> diff --git a/DolphinEnglishLearnStudent/Moudle/Me/MeVC.swift b/DolphinEnglishLearnStudent/Moudle/Me/MeVC.swift index ec4ed38..449d943 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/MeVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Me/MeVC.swift @@ -14,11 +14,22 @@ @IBOutlet weak var btn_exchangeRecord: QMUIButton! @IBOutlet weak var btn_share: QMUIButton! @IBOutlet weak var btn_etudyRecord: QMUIButton! - + @IBOutlet weak var imge_cover: UIImageView! + @IBOutlet weak var label_name: UILabel! + @IBOutlet weak var label_info: UILabel! + override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view. + Services.userInfo().subscribe(onNext: {result in + if let model = result.data{ + self.imge_cover.sd_setImage(with: URL(string: model.headImg)) + self.label_name.text = model.name +// var items = Array<String>() +// items.append("剩余积分:\(model.integral)") +// items.append("学习进度:\(model.integral)") + } + }).disposed(by: disposeBag) } @@ -69,7 +80,9 @@ @IBAction func quitAction(_ sender: UIButton) { CommonAlertView.show(content: "确认退出当前账户吗?") { - + Services.logoutStudy().subscribe(onNext: {result in + sceneDelegate?.needLogin() + }).disposed(by: self.disposeBag) } } } diff --git a/DolphinEnglishLearnStudent/Moudle/Me/MeVC.xib b/DolphinEnglishLearnStudent/Moudle/Me/MeVC.xib index 76037b8..47ebb3b 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/MeVC.xib +++ b/DolphinEnglishLearnStudent/Moudle/Me/MeVC.xib @@ -16,6 +16,9 @@ <outlet property="btn_etudyRecord" destination="0pk-gO-3Qh" id="SeS-bo-pn4"/> <outlet property="btn_exchangeRecord" destination="uH5-eT-V9Z" id="8gd-zT-JND"/> <outlet property="btn_share" destination="gtG-mF-MKi" id="ejw-y9-ugq"/> + <outlet property="imge_cover" destination="qXF-FL-HEr" id="fG7-3a-cHy"/> + <outlet property="label_info" destination="LJb-Ki-p3S" id="88F-Ay-1nv"/> + <outlet property="label_name" destination="D0d-O7-Pt2" id="C7j-gd-GwB"/> <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> </connections> </placeholder> diff --git a/DolphinEnglishLearnStudent/Moudle/Me/TCell/AddressManageTCell.swift b/DolphinEnglishLearnStudent/Moudle/Me/TCell/AddressManageTCell.swift index c5564b3..add306d 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/TCell/AddressManageTCell.swift +++ b/DolphinEnglishLearnStudent/Moudle/Me/TCell/AddressManageTCell.swift @@ -6,6 +6,7 @@ // import UIKit +import RxSwift class AddressManageTCell: UITableViewCell { @@ -15,7 +16,19 @@ @IBOutlet weak var btn_delete: UIButton! @IBOutlet weak var btn_edit: UIButton! @IBOutlet weak var img_more: UIImageView! - + @IBOutlet weak var label_address: UILabel! + @IBOutlet weak var label_addressInfo: UILabel! + + private var disposeBag = DisposeBag() + + var addressModel:AddressModel!{ + didSet{ + label_address.text = addressModel.address + label_addressInfo.text = addressModel.recipient + "|" + addressModel.recipientPhone + isDefault(addressModel.isDefault == 1) + } + } + override func awakeFromNib() { super.awakeFromNib() selectionStyle = .none @@ -24,6 +37,10 @@ func isDefault(_ state:Bool){ if state{ + + let attribute = AttributedStringbuilder.build().add(string:" 默认 ", withFont: .systemFont(ofSize: 14, weight: .medium), withColor: UIColor.white).mutableAttributedString + btn_default.setAttributedTitle(attribute, for: .normal) + btn_default.setTitle(" 默认 ", for: .normal) btn_default.backgroundColor = UIColor(hexStr: "#F7462D") btn_default.setTitleColor(.white, for: .normal) @@ -37,13 +54,23 @@ } @IBAction func deleteAction(_ sender: UIButton) { - CommonAlertView.show(content:"确认删除所选信息吗?") { - + CommonAlertView.show(content:"确认删除所选信息吗?") {[weak self] () in + guard let weakSelf = self else { return } + Services.deleteAddress(id: weakSelf.addressModel.id).subscribe(onNext: { _ in + NotificationCenter.default.post(name: AddressManage_Refresh_Noti, object: nil) + }).disposed(by: weakSelf.disposeBag) } } + + @IBAction func setDefaultAction(_ sender: UIButton) { + Services.setDefaultStudy(id: addressModel.id).subscribe(onNext: { _ in + NotificationCenter.default.post(name: AddressManage_Refresh_Noti, object: nil) + }).disposed(by: disposeBag) + } + @IBAction func editAction(_ sender: UIButton) { - let vc = AddressManageHandleVC() + let vc = AddressManageHandleVC(addressModel) vc.title = "编辑地址" JQ_currentViewController().jq_push(vc: vc) } @@ -57,5 +84,9 @@ contentView.jq_addCorners(corner: [.bottomLeft,.bottomLeft], radius: 10) } + if isFist && isLast{ + contentView.jq_addCorners(corner: .allCorners, radius: 10) + } + } } diff --git a/DolphinEnglishLearnStudent/Moudle/Me/TCell/AddressManageTCell.xib b/DolphinEnglishLearnStudent/Moudle/Me/TCell/AddressManageTCell.xib index 03c61f5..aef8819 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/TCell/AddressManageTCell.xib +++ b/DolphinEnglishLearnStudent/Moudle/Me/TCell/AddressManageTCell.xib @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> - <device id="retina6_12" orientation="portrait" appearance="light"/> + <device id="ipad10_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/> <dependencies> <deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/> @@ -10,7 +10,7 @@ <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="95" id="KGk-i7-Jjw" customClass="AddressManageTCell" customModule="DolphinEnglishLearnManager" customModuleProvider="target"> + <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="95" id="KGk-i7-Jjw" customClass="AddressManageTCell" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="447" height="95"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM"> @@ -27,7 +27,7 @@ <nil key="highlightedColor"/> </label> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ezc-re-P3r"> - <rect key="frame" x="375" y="18.666666666666668" width="58" height="17.000000000000004"/> + <rect key="frame" x="375" y="18.5" width="58" height="17"/> <constraints> <constraint firstAttribute="height" constant="17" id="0Kf-PA-7Or"/> <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="42" id="4RT-3H-dH2"/> @@ -37,9 +37,12 @@ <state key="normal" title="设为默认"> <color key="titleColor" red="0.25490196079999999" green="0.63529411759999999" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </state> + <connections> + <action selector="setDefaultAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="p3w-8e-C9z"/> + </connections> </button> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="-- | --" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="g9x-qe-0Me"> - <rect key="frame" x="13.999999999999996" y="42" width="41.666666666666657" height="17"/> + <rect key="frame" x="14" y="42" width="42" height="17"/> <constraints> <constraint firstAttribute="height" constant="17" id="04w-Er-l9A"/> </constraints> @@ -77,7 +80,7 @@ </connections> </button> <imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_more_gray" translatesAutoresizingMaskIntoConstraints="NO" id="Qav-bf-Mlo"> - <rect key="frame" x="427" y="42.666666666666664" width="6" height="10"/> + <rect key="frame" x="427" y="42.5" width="6" height="10"/> </imageView> </subviews> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> @@ -106,6 +109,8 @@ <outlet property="btn_delete" destination="a8n-tE-gqa" id="elJ-VF-A4k"/> <outlet property="btn_edit" destination="WFf-ch-GdK" id="Wfz-Oo-mbZ"/> <outlet property="img_more" destination="Qav-bf-Mlo" id="ykH-wg-Brt"/> + <outlet property="label_address" destination="Qjx-I2-glv" id="iwX-fL-7LK"/> + <outlet property="label_addressInfo" destination="g9x-qe-0Me" id="cZT-hR-szg"/> </connections> <point key="canvasLocation" x="202.29007633587784" y="49.647887323943664"/> </tableViewCell> diff --git a/DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.swift b/DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.swift index da004ec..6dc7fa9 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.swift +++ b/DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.swift @@ -8,7 +8,16 @@ import UIKit class GoodsItemTCell: UITableViewCell { - + + @IBOutlet weak var label_state: UILabel! + @IBOutlet weak var label_goodsName: UILabel! + @IBOutlet weak var label_types: UILabel! + @IBOutlet weak var label_goodsNum: UILabel! + @IBOutlet weak var label_receiptInfo: UILabel! + @IBOutlet weak var label_sendInfo: UILabel! + @IBOutlet weak var btn_state: UIButton! + @IBOutlet weak var label_coin: UILabel! + @IBOutlet weak var view_container: UIView! override func awakeFromNib() { super.awakeFromNib() @@ -17,6 +26,24 @@ view_container.jq_addShadows(shadowColor: UIColor(hexStr: "#D9D9D9").withAlphaComponent(0.28), corner: 8, radius: 3, offset: CGSize(width: 0, height: 2), opacity: 1) } + func setModel(_ model:ExchangeRecordModel){ + label_goodsNum.text = "商品数量:\(model.count)" + label_coin.text = "\(model.integral)积分" + + var items_consignee = Array<String>() + items_consignee.append(model.consigneeName) + items_consignee.append(model.consigneePhone) + items_consignee.append(model.consigneeAddress) + label_receiptInfo.text = "收货信息:" + items_consignee.joined(separator: "|") + + var items_express = Array<String>() + items_express.append(model.express) + items_express.append(model.expressNumber) + + label_sendInfo.isHidden = items_express.filter({!$0.isEmpty}).count == 0 + label_sendInfo.text = "发货信息:" + items_express.joined(separator: "|") + } + @IBAction func handleAction(_ sender: UIButton) { let vc = AddressManageVC(type: .choose) vc.title = "修改地址" diff --git a/DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.xib b/DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.xib index ffc3dcf..938ac98 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.xib +++ b/DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.xib @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> - <device id="retina6_12" orientation="portrait" appearance="light"/> + <device id="ipad10_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/> <dependencies> <deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/> @@ -10,7 +10,7 @@ <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="_GoodsItemTCell" rowHeight="344" id="KGk-i7-Jjw" customClass="GoodsItemTCell" customModule="DolphinEnglishLearnManager" customModuleProvider="target"> + <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="_GoodsItemTCell" rowHeight="344" id="KGk-i7-Jjw" customClass="GoodsItemTCell" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="532" height="344"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM"> @@ -21,13 +21,13 @@ <rect key="frame" x="0.0" y="6" width="532" height="332"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9oA-7x-in6"> - <rect key="frame" x="8" y="19" width="41.333333333333336" height="20.333333333333329"/> + <rect key="frame" x="8" y="19" width="41.5" height="20.5"/> <fontDescription key="fontDescription" type="system" pointSize="17"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="HZS-Oe-5LR"> - <rect key="frame" x="8" y="52.333333333333343" width="148" height="148"/> + <rect key="frame" x="8" y="52.5" width="148" height="148"/> <constraints> <constraint firstAttribute="width" constant="148" id="D3I-iy-k6w"/> <constraint firstAttribute="width" secondItem="HZS-Oe-5LR" secondAttribute="height" multiplier="1:1" id="YQJ-eG-71N"/> @@ -40,25 +40,25 @@ </userDefinedRuntimeAttributes> </imageView> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UdI-oe-WSO"> - <rect key="frame" x="173" y="52.333333333333336" width="234" height="19.333333333333336"/> + <rect key="frame" x="173" y="52.5" width="234" height="19.5"/> <fontDescription key="fontDescription" type="system" pointSize="16"/> <color key="textColor" red="0.082352941176470587" green="0.086274509803921567" blue="0.094117647058823528" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <nil key="highlightedColor"/> </label> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0积分" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nMG-SO-H2Z"> - <rect key="frame" x="481" y="52.666666666666664" width="40" height="18.666666666666664"/> + <rect key="frame" x="481" y="53" width="40" height="19"/> <fontDescription key="fontDescription" name="DINAlternate-Bold" family="DIN Alternate" pointSize="16"/> <color key="textColor" red="0.96862745100000003" green="0.27450980390000002" blue="0.1764705882" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <nil key="highlightedColor"/> </label> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="viA-yT-ZWk"> - <rect key="frame" x="173" y="71.666666666666671" width="359" height="17"/> + <rect key="frame" x="173" y="72" width="359" height="17"/> <fontDescription key="fontDescription" type="system" pointSize="14"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="vd5-iK-Ujb"> - <rect key="frame" x="173" y="95.666666666666686" width="303" height="70"/> + <rect key="frame" x="173" y="96" width="303" height="70"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="商品数量:-" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PlF-D7-xZD"> <rect key="frame" x="0.0" y="0.0" width="303" height="20"/> @@ -79,7 +79,7 @@ <nil key="highlightedColor"/> </label> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="发货信息:-" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cSS-lo-Yba"> - <rect key="frame" x="0.0" y="49.999999999999986" width="303" height="20"/> + <rect key="frame" x="0.0" y="50" width="303" height="20"/> <constraints> <constraint firstAttribute="height" constant="20" id="mDC-DF-Wc3"/> </constraints> @@ -141,6 +141,14 @@ </tableViewCellContentView> <viewLayoutGuide key="safeArea" id="njF-e1-oar"/> <connections> + <outlet property="btn_state" destination="xwt-lH-fyW" id="YNc-qk-0gB"/> + <outlet property="label_coin" destination="nMG-SO-H2Z" id="weP-sJ-QLG"/> + <outlet property="label_goodsName" destination="UdI-oe-WSO" id="o9d-iH-BCJ"/> + <outlet property="label_goodsNum" destination="PlF-D7-xZD" id="1mj-HF-LGA"/> + <outlet property="label_receiptInfo" destination="qnF-SM-ngM" id="me5-fe-eW2"/> + <outlet property="label_sendInfo" destination="cSS-lo-Yba" id="4fa-fc-f69"/> + <outlet property="label_state" destination="9oA-7x-in6" id="lBa-OB-S8W"/> + <outlet property="label_types" destination="viA-yT-ZWk" id="3dU-h2-97f"/> <outlet property="view_container" destination="EWl-m5-3O9" id="vyU-l1-0CY"/> </connections> <point key="canvasLocation" x="259.5419847328244" y="126.05633802816902"/> diff --git a/DolphinEnglishLearnStudent/Moudle/Me/TCell/Home_1_TCell.swift b/DolphinEnglishLearnStudent/Moudle/Me/TCell/Home_1_TCell.swift index c834219..545162a 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/TCell/Home_1_TCell.swift +++ b/DolphinEnglishLearnStudent/Moudle/Me/TCell/Home_1_TCell.swift @@ -9,6 +9,26 @@ class Home_1_TCell: UITableViewCell { + @IBOutlet weak var label_1: UILabel! + @IBOutlet weak var label_2: UILabel! + @IBOutlet weak var label_3: UILabel! + @IBOutlet weak var label_4: UILabel! + + var integralModel:IntegralModel!{ + didSet{ + label_1.text = integralModel.createTime + label_2.text = integralModel.integral + label_3.text = integralModel.method + label_4.text = integralModel.type + } + } + + var studyGamesRecordModel:StudyGamesRecordModel!{ + didSet{ + + } + } + override func awakeFromNib() { super.awakeFromNib() selectionStyle = .none diff --git a/DolphinEnglishLearnStudent/Moudle/Me/TCell/Home_1_TCell.xib b/DolphinEnglishLearnStudent/Moudle/Me/TCell/Home_1_TCell.xib index fdf2c39..a28f030 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/TCell/Home_1_TCell.xib +++ b/DolphinEnglishLearnStudent/Moudle/Me/TCell/Home_1_TCell.xib @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> - <device id="retina6_12" orientation="portrait" appearance="light"/> + <device id="ipad10_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/> <dependencies> <deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/> @@ -9,7 +9,7 @@ <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="_Home_1_TCell" id="KGk-i7-Jjw" customClass="Home_1_TCell" customModule="DolphinEnglishLearnManager" customModuleProvider="target"> + <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="_Home_1_TCell" id="KGk-i7-Jjw" customClass="Home_1_TCell" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="320" height="44"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM"> @@ -53,6 +53,12 @@ <constraint firstItem="xga-Br-Izz" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="dHd-ao-jYA"/> </constraints> </tableViewCellContentView> + <connections> + <outlet property="label_1" destination="LLg-mN-hnD" id="7UE-le-ZJZ"/> + <outlet property="label_2" destination="bXA-iA-Sgc" id="4He-VH-alj"/> + <outlet property="label_3" destination="tft-ra-5If" id="CLY-lE-hEm"/> + <outlet property="label_4" destination="UQ2-uJ-GO9" id="nNZ-Gp-UJ9"/> + </connections> <point key="canvasLocation" x="139" y="21"/> </tableViewCell> </objects> diff --git a/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageHandleVC.swift b/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageHandleVC.swift index 5d7ced4..2f28e14 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageHandleVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageHandleVC.swift @@ -6,17 +6,119 @@ // import UIKit +import QMUIKit +import RxRelay + +class AddressManageHandleViewModel{ + var userName = BehaviorRelay<String?>(value: nil) + var phone = BehaviorRelay<String?>(value: nil) + var provinceModel = BehaviorRelay<AddressTreeModel?>(value: nil) + var cityModel = BehaviorRelay<AddressTreeModel?>(value: nil) + var countryModel = BehaviorRelay<AddressTreeModel?>(value: nil) + var detailAddress = BehaviorRelay<String?>(value: nil) + var isDefault = BehaviorRelay<Bool>(value: false) +} class AddressManageHandleVC: BaseVC { + + @IBOutlet weak var tf_name: UITextField! + @IBOutlet weak var tf_phone: QMUITextField! + @IBOutlet weak var tf_address: QMUITextField! + @IBOutlet weak var tf_addressContent: QMUITextField! + @IBOutlet weak var btn_isDefault: UIButton! + + let viewModel = AddressManageHandleViewModel() + private var updateModel:AddressModel? @IBOutlet weak var view_container: UIView! override func viewDidLoad() { super.viewDidLoad() + if let updateModel{ + tf_name.text = updateModel.recipient + tf_phone.text = updateModel.recipientPhone + tf_address.text = updateModel.province + updateModel.city + tf_addressContent.text = updateModel.address + btn_isDefault.isSelected = updateModel.isDefault == 1 + + viewModel.userName.accept(updateModel.recipient) + viewModel.phone.accept(updateModel.recipientPhone) + viewModel.detailAddress.accept(updateModel.address) + + viewModel.provinceModel.accept(AddressTreeModel(id: 0, name: updateModel.province, code: updateModel.provinceCode, parentId: 0, children: nil)) + viewModel.cityModel.accept(AddressTreeModel(id: 0, name: updateModel.city, code: updateModel.cityCode, parentId: 0, children: nil)) + } } + init(_ updateModel:AddressModel? = nil) { + super.init(nibName: nil, bundle: nil) + self.updateModel = updateModel + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func setUI() { super.setUI() view_container.jq_addShadows(shadowColor: UIColor(hexStr: "#D9D9D9").withAlphaComponent(0.28), corner: 8, radius: 20, offset: CGSize(width: 0, height: 2), opacity: 1) + tf_address.delegate = self + } + + override func setRx() { + tf_name.rx.text.bind(to: viewModel.userName).disposed(by: disposeBag) + tf_phone.rx.text.bind(to: viewModel.phone).disposed(by: disposeBag) + tf_addressContent.rx.text.bind(to: viewModel.detailAddress).disposed(by: disposeBag) + } + + @IBAction func chooseDefaultAction(_ sender: UIButton) { + sender.isSelected = !sender.isSelected + } + + @IBAction func saveAction(_ sender: UIButton) { + guard !(viewModel.userName.value?.isEmpty ?? true) else { + alert(msg: "请输入收件人姓名");return + } + + guard !(viewModel.phone.value?.isEmpty ?? true) else { + alert(msg: "请输入收件人电话");return + } + + guard viewModel.phone.value!.jq_isPhone else { + alert(msg: "请输入正确的手机号码");return + } + + guard viewModel.cityModel.value != nil else { + alert(msg: "请选择所在城市");return + } + + guard !(viewModel.detailAddress.value?.isEmpty ?? true) else { + alert(msg: "请输入详细地址");return + } + + Services.addressSaveOrUpdate(id: updateModel?.id, province: viewModel.provinceModel.value, city: viewModel.cityModel.value, country: nil, userName: viewModel.userName.value, userPhone: viewModel.phone.value, isDefault: btn_isDefault.isSelected, detailAddress: viewModel.detailAddress.value ?? "").subscribe(onNext: {_ in + if self.updateModel != nil{ + alertSuccess(msg: "编辑成功") + }else{ + alertSuccess(msg: "添加成功") + } + + self.navigationController?.popViewController() + NotificationCenter.default.post(name: AddressManage_Refresh_Noti, object: nil) + }).disposed(by: disposeBag) + } +} + +extension AddressManageHandleVC:QMUITextFieldDelegate{ + + func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { + CityAddressPickerView.show(title: "选择省市区") {[weak self] province, city, country in + self?.viewModel.provinceModel.accept(province) + self?.viewModel.cityModel.accept(city) + self?.viewModel.countryModel.accept(country) + + textField.text = "\(province?.name ?? "") \(city?.name ?? "") \(country?.name ?? "")" + } + return false } } diff --git a/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageHandleVC.xib b/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageHandleVC.xib index 7b8ca0f..b97b7a9 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageHandleVC.xib +++ b/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageHandleVC.xib @@ -11,6 +11,11 @@ <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AddressManageHandleVC" customModule="DolphinEnglishLearnStudent" customModuleProvider="target"> <connections> + <outlet property="btn_isDefault" destination="X5q-Za-IDi" id="6lr-qr-5nr"/> + <outlet property="tf_address" destination="FIR-Ti-oMo" id="XyJ-kk-oGH"/> + <outlet property="tf_addressContent" destination="9Rg-uR-zdR" id="sxn-jk-kYJ"/> + <outlet property="tf_name" destination="oWU-h5-9OT" id="9Gb-Hg-ka1"/> + <outlet property="tf_phone" destination="fLZ-09-hXt" id="YZs-at-Zeo"/> <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> <outlet property="view_container" destination="qgb-Bj-4P0" id="eaY-Jn-MsE"/> </connections> @@ -151,7 +156,7 @@ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eNK-zb-eqQ"> <rect key="frame" x="0.0" y="162" width="888" height="54"/> <subviews> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="所在城市" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uwd-8E-QFK"> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="详细地址" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uwd-8E-QFK"> <rect key="frame" x="14" y="18.5" width="57.5" height="17"/> <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/> <nil key="textColor"/> @@ -203,6 +208,9 @@ <color key="titleColor" red="0.0" green="0.0" blue="0.0" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="sRGB"/> </state> <state key="selected" title="设为默认" image="btn_choose"/> + <connections> + <action selector="chooseDefaultAction:" destination="-1" eventType="touchUpInside" id="6aM-GW-eHa"/> + </connections> </button> </subviews> <color key="backgroundColor" systemColor="systemBackgroundColor"/> @@ -232,6 +240,9 @@ <real key="value" value="8"/> </userDefinedRuntimeAttribute> </userDefinedRuntimeAttributes> + <connections> + <action selector="saveAction:" destination="-1" eventType="touchUpInside" id="Ml0-tI-QPP"/> + </connections> </button> </subviews> <viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/> diff --git a/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageVC.swift b/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageVC.swift index 40c70bd..b5bb5d2 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Me/VC/AddressManageVC.swift @@ -6,6 +6,15 @@ // import UIKit +import RxSwift + +let AddressManage_Refresh_Noti = Notification.Name.init("AddressManage_Refresh_Noti") + +class AddressManageViewModel:RefreshModel<AddressModel>{ + override func api() -> (Observable<BaseResponse<[AddressModel]>>)? { + Services.addressList() + } +} class AddressManageVC: BaseVC { @@ -16,6 +25,7 @@ private var tableView:UITableView! private var addressManageType:AddressManageType! + private var viewModel = AddressManageViewModel() required init(type:AddressManageType) { super.init(nibName: nil, bundle: nil) @@ -28,6 +38,9 @@ override func viewDidLoad() { super.viewDidLoad() + + viewModel.configure(tableView) + viewModel.beginRefresh() } @@ -73,6 +86,12 @@ } } + override func setRx() { + NotificationCenter.default.rx.notification(AddressManage_Refresh_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self] _ in + self?.viewModel.beginRefresh() + }).disposed(by: disposeBag) + } + @objc func handleAction(){ let vc = AddressManageHandleVC() vc.title = "地址管理" @@ -86,10 +105,11 @@ extension AddressManageVC:UITableViewDataSource{ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let model = viewModel.dataSource.value[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "_AddressManageTCell") as! AddressManageTCell + cell.addressModel = model cell.isFist = indexPath.row == 0 - cell.isLast = indexPath.row == 9 - cell.isDefault(indexPath.row == 0) + cell.isLast = indexPath.row == viewModel.dataSource.value.count - 1 cell.btn_edit.isHidden = addressManageType == .choose cell.btn_delete.isHidden = addressManageType == .choose @@ -100,7 +120,7 @@ } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 10 + return viewModel.dataSource.value.count } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { diff --git a/DolphinEnglishLearnStudent/Moudle/Me/VC/CoinRecordHistoryVC.swift b/DolphinEnglishLearnStudent/Moudle/Me/VC/CoinRecordHistoryVC.swift index e38ece0..6db38ea 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/VC/CoinRecordHistoryVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Me/VC/CoinRecordHistoryVC.swift @@ -7,9 +7,15 @@ import UIKit import RxRelay +import JQTools +import RxSwift -class CoinRecordHistoryViewModel{ +class CoinRecordHistoryViewModel:RefreshInnerModel<IntegralModel>{ var selectDate = BehaviorRelay<Date?>(value:nil) + + override func api() -> (Observable<BaseResponse<BaseResponseList<IntegralModel>>>)? { + return Services.integralDetail(pageNum: page, time: selectDate.value?.jq_format("yyyy-MM")) + } } class CoinRecordHistoryVC: BaseVC { @@ -23,6 +29,12 @@ override func viewDidLoad() { super.viewDidLoad() + viewModel.configure(tableView,needMore: true) + viewModel.beginRefresh() + + Services.getIntegral().subscribe(onNext: {reault in + self.label_coin.text = "\(reault.data ?? 0)" + }).disposed(by: disposeBag) } override func setUI() { @@ -43,13 +55,19 @@ }).disposed(by: disposeBag) } - @IBAction func chooseDateTimeAction(_ sender: UIButton) { let year = viewModel.selectDate.value?.jq_nowYear() ?? Date().jq_nowYear() let month = viewModel.selectDate.value?.jq_nowMonth() ?? Date().jq_nowMonth() BitrhdayPickerView.show(title: "查询时间", type: .YM, defaultYear: year, defaultMonth: month, defaultDay: 0, minYear: 0) {[weak self] date in self?.viewModel.selectDate.accept(date) + self?.viewModel.beginRefresh() } + } + + @IBAction func resetAction(_ sender: UIButton) { + viewModel.selectDate.accept(nil) + btn_selectDate.setTitle("请选择", for: .normal) + viewModel.beginRefresh() } } @@ -67,11 +85,13 @@ cell.contentView.backgroundColor = .white } + let m = viewModel.dataSource.value?.records[indexPath.row] + cell.integralModel = m return cell } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 5 + return viewModel.dataSource.value?.records.count ?? 0 } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { diff --git a/DolphinEnglishLearnStudent/Moudle/Me/VC/CoinRecordHistoryVC.xib b/DolphinEnglishLearnStudent/Moudle/Me/VC/CoinRecordHistoryVC.xib index 1f5cb6f..48f0fe3 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/VC/CoinRecordHistoryVC.xib +++ b/DolphinEnglishLearnStudent/Moudle/Me/VC/CoinRecordHistoryVC.xib @@ -74,6 +74,9 @@ </constraints> <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> <state key="normal" image="btn_refresh"/> + <connections> + <action selector="resetAction:" destination="-1" eventType="touchUpInside" id="UoC-8u-vu0"/> + </connections> </button> </subviews> <constraints> diff --git a/DolphinEnglishLearnStudent/Moudle/Me/VC/ExchangeRecordHistoryVC.swift b/DolphinEnglishLearnStudent/Moudle/Me/VC/ExchangeRecordHistoryVC.swift index 583e8ac..1a29931 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/VC/ExchangeRecordHistoryVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Me/VC/ExchangeRecordHistoryVC.swift @@ -6,14 +6,25 @@ // import UIKit +import RxSwift + +class ExchangeRecordViewModel:RefreshModel<ExchangeRecordModel>{ + override func api() -> (Observable<BaseResponse<[ExchangeRecordModel]>>)? { + return Services.exchangeRecord() + } +} class ExchangeRecordHistoryVC: BaseVC { + + private let viewModel = ExchangeRecordViewModel() private var tableView:UITableView! override func viewDidLoad() { super.viewDidLoad() + viewModel.configure(tableView) + viewModel.beginRefresh() } override func setUI() { @@ -42,11 +53,12 @@ extension ExchangeRecordHistoryVC:UITableViewDataSource{ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "_GoodsItemTCell") as! GoodsItemTCell + cell.setModel(viewModel.dataSource.value[indexPath.row]) return cell } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 10 + return viewModel.dataSource.value.count } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { diff --git a/DolphinEnglishLearnStudent/Moudle/Me/VC/StudyVC.swift b/DolphinEnglishLearnStudent/Moudle/Me/VC/StudyVC.swift index a4daf6e..ed38a0c 100644 --- a/DolphinEnglishLearnStudent/Moudle/Me/VC/StudyVC.swift +++ b/DolphinEnglishLearnStudent/Moudle/Me/VC/StudyVC.swift @@ -11,9 +11,21 @@ @IBOutlet weak var view_menu: UIView! @IBOutlet weak var tableView: UITableView! + private var gamesRecordModel:StudyGamesModel?{ + didSet{ + self.tableView.reloadData() + } + } + + override func viewDidLoad() { super.viewDidLoad() + Services.studyGamesRecord().subscribe(onNext: {result in + if let m = result.data{ + self.gamesRecordModel = m + } + }).disposed(by: disposeBag) } override func setUI() { @@ -44,12 +56,16 @@ }else{ cell.contentView.backgroundColor = .white } + if let m = gamesRecordModel?.gameRecordList[indexPath.row]{ + cell.studyGamesRecordModel = m + } + return cell } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 5 + return gamesRecordModel?.gameRecordList.count ?? 0 } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { diff --git a/DolphinEnglishLearnStudent/Other/CommonWebVC.swift b/DolphinEnglishLearnStudent/Other/CommonWebVC.swift index 2bc0e18..e2d4f11 100644 --- a/DolphinEnglishLearnStudent/Other/CommonWebVC.swift +++ b/DolphinEnglishLearnStudent/Other/CommonWebVC.swift @@ -10,16 +10,17 @@ class CommonWebVC: BaseVC { - enum CommonWebType{ - case logoff - case userAgreement - case privacyAgreement - case userGuide - } + private var type:AgreementType! - private var type:CommonWebType! + private lazy var webView:WKWebView = { + let webView = WKWebView() + webView.backgroundColor = .clear + webView.scrollView.backgroundColor = .clear + webView.isOpaque = false + return webView + }() - init(type:CommonWebType) { + init(type:AgreementType) { super.init(nibName: nil, bundle: nil) self.type = type } @@ -36,8 +37,14 @@ override func setUI() { super.setUI() + view.addSubview(webView) + webView.snp.makeConstraints { make in + make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top) + make.left.right.equalToSuperview() + make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom) + } - if type == .logoff{ + if type == .logout{ let completeBtn = UIButton(type: .custom) completeBtn.jq_cornerRadius = 8 completeBtn.addTarget(self, action: #selector(handleAction), for: .touchUpInside) @@ -76,6 +83,13 @@ }); timer.resume() } + + + Services.getAgreement(type: type).subscribe(onNext: {[weak self]content in + if let content = content.data{ + self?.webView.loadHTMLString(content.jq_wrapHtml(), baseURL: nil) + } + }).disposed(by: disposeBag) } @objc func handleAction(sender:UIButton){ diff --git a/DolphinEnglishLearnStudent/Other/UIView/CityAddressPickerView.swift b/DolphinEnglishLearnStudent/Other/UIView/CityAddressPickerView.swift new file mode 100644 index 0000000..4b78330 --- /dev/null +++ b/DolphinEnglishLearnStudent/Other/UIView/CityAddressPickerView.swift @@ -0,0 +1,248 @@ +// +// CityAddressPickerView.swift +// DolphinEnglishLearnStudent +// +// Created by 无故事王国 on 2024/6/3. +// + +import UIKit +import RxSwift + +class CityAddressPickerView: UIView { + + private var disposeBag = DisposeBag() + private var items = [AddressTreeModel]() + private var clouse:((AddressTreeModel?,AddressTreeModel?,AddressTreeModel?)->Void)! + + private var view_content:UIView = { + let v = UIView() + v.backgroundColor = .white + return v + }() + + private var label_title:UILabel = { + let label = UILabel() + label.font = .systemFont(ofSize: 18, weight: .medium) + label.textColor = .black.withAlphaComponent(0.8) + return label + }() + + private var btn_close:UIButton = { + let btn = UIButton(type: .custom) + btn.setImage(UIImage(named: "btn_close_circle"), for: .normal) + return btn + }() + + private var btn_complete:UIButton = { + let btn = UIButton(type: .custom) + btn.setTitle("确认", for: .normal) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium) + btn.setTitleColor(.white, for: .normal) + btn.backgroundColor = Config.ThemeColor + btn.jq_cornerRadius = 8 + return btn + }() + + + private var picker:UIPickerView = { + let pickerView = UIPickerView() + return pickerView + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = UIColor.black.withAlphaComponent(0) + addSubview(view_content) + view_content.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalToSuperview().offset(JQ_ScreenW * 0.3278) + make.height.equalTo(JQ_ScreenW * 0.3278) + } + + + view_content.addSubview(btn_close) + btn_close.addTarget(self, action: #selector(closeAction), for: .touchUpInside) + btn_close.snp.makeConstraints { make in + make.top.equalTo(10) + make.right.equalTo(-11) + make.width.height.equalTo(23) + } + + view_content.addSubview(label_title) + label_title.snp.makeConstraints { make in + make.top.equalTo(30) + make.centerX.equalToSuperview() + make.height.equalTo(18) + } + + layoutIfNeeded() + } + + private func setUI(){ + var items = Array<String>() + items.append("省") + items.append("市") + items.append("区") + + let stackView = UIStackView() + stackView.axis = .horizontal + stackView.distribution = .fillEqually + stackView.spacing = 133 + view_content.addSubview(stackView) + stackView.snp.makeConstraints { make in + make.top.equalTo(label_title.snp.bottom).offset(27) + make.height.equalTo(25) + make.centerX.equalToSuperview() + } + + for (_,v) in items.enumerated(){ + let label = UILabel() + label.text = v + label.font = UIFont.systemFont(ofSize: 18, weight: .medium) + label.textColor = .black.withAlphaComponent(0.8) + label.textAlignment = .center + stackView.addArrangedSubview(label) + } + + view_content.addSubview(btn_complete) + btn_complete.snp.makeConstraints { make in + make.bottom.equalToSuperview().offset(-UIDevice.jq_safeEdges.bottom) + make.centerX.equalToSuperview() + make.width.equalTo(JQ_ScreenW * 0.1487) + make.height.equalTo(47) + } + + view_content.addSubview(picker) + picker.delegate = self + picker.dataSource = self + picker.snp.makeConstraints { make in + make.top.equalTo(label_title.snp.bottom).offset(50) + make.bottom.equalTo(btn_complete.snp.top) + make.centerX.equalToSuperview() + make.width.equalTo(500) + } + + btn_complete.addTarget(self, action: #selector(completeAction), for: .touchUpInside) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + static func show(title:String,clouse:@escaping (AddressTreeModel?,AddressTreeModel?,AddressTreeModel?)->Void){ + let pickerView = CityAddressPickerView(frame: sceneDelegate?.window?.frame ?? .zero) + pickerView.clouse = clouse + sceneDelegate?.window?.addSubview(pickerView) + pickerView.setUI() + + UIView.animate(withDuration: 0.35) { + pickerView.backgroundColor = UIColor.black.withAlphaComponent(0.7) + pickerView.view_content.snp.updateConstraints { make in + make.bottom.equalToSuperview() + } + pickerView.layoutIfNeeded() + }completion: { _ in + Services.addressTree().subscribe(onNext: {data in + pickerView.items = data.data ?? [] + pickerView.picker.reloadAllComponents() + }).disposed(by: pickerView.disposeBag) + } + } + + @objc func closeAction(){ + UIView.animate(withDuration: 0.35) { + self.backgroundColor = UIColor.black.withAlphaComponent(0) + self.view_content.snp.updateConstraints { make in + make.bottom.equalToSuperview().offset(JQ_ScreenW * 0.3278) + } + self.layoutIfNeeded() + }completion: { _ in + self.removeFromSuperview() + } + } + + @objc func completeAction(){ + + let provinceIndex = picker.selectedRow(inComponent: 0) + let provinceModel = items[provinceIndex] + let cityModel = items[provinceIndex].children?[picker.selectedRow(inComponent: 1)] + let countryModel = items[provinceIndex].children?[picker.selectedRow(inComponent: 1)].children?[picker.selectedRow(inComponent: 2)] + clouse(provinceModel,cityModel,countryModel) + closeAction() + } +} + +extension CityAddressPickerView:UIPickerViewDelegate & UIPickerViewDataSource{ + func numberOfComponents(in pickerView: UIPickerView) -> Int { + if items.count > 0{ + return 3 + } + return 0 + + } + + func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { + return 40 + } + + func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { + + var resultLabel:UILabel? + if let label = view as? UILabel{ + resultLabel = label + }else{ + resultLabel = UILabel() + resultLabel!.font = UIFont.systemFont(ofSize: 18, weight: .semibold) + resultLabel!.textColor = UIColor(hexStr: "#3C3C3C") + resultLabel!.textAlignment = .center + } + + if component == 0{ + resultLabel!.text = items[row].name + } + + if component == 1{ + let v = items[pickerView.selectedRow(inComponent: 0)] + resultLabel!.text = v.children?[row].name ?? "" + } + + if component == 2{ + let v = items[pickerView.selectedRow(inComponent: 0)] + let v1 = v.children?[pickerView.selectedRow(inComponent: 1)] + resultLabel!.text = v1?.children?[row].name ?? "" + } + + return resultLabel! + } + + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + if component == 0 { + return items.count + } + + if component == 1{ + let index = pickerView.selectedRow(inComponent: 0) + return items[index].children?.count ?? 0 + } + + if component == 2{ + let indexI = pickerView.selectedRow(inComponent: 0) + let indexJ = pickerView.selectedRow(inComponent: 1) + return items[indexI].children?[indexJ].children?.count ?? 0 + } + + return 0 + } + + func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + if component == 0{ + pickerView.reloadComponent(1) + pickerView.reloadComponent(2) + } + + if component == 1{ + pickerView.reloadComponent(2) + } + } +} diff --git a/DolphinEnglishLearnStudent/Other/UIView/CommonBannerView.swift b/DolphinEnglishLearnStudent/Other/UIView/CommonBannerView.swift new file mode 100644 index 0000000..b7117d0 --- /dev/null +++ b/DolphinEnglishLearnStudent/Other/UIView/CommonBannerView.swift @@ -0,0 +1,208 @@ +// +// CommonBannerView.swift +// WanPai +// +// Created by 无故事王国 on 2023/6/30. +// + +import UIKit +import SDWebImage +struct CommonBannerModel { + var index = 0 //自定义索引 + var id:Int? //ID + var name:String? //名称 + var resource:String? //数据源:URL等 + var mediaType:CommonBannerView.MediaType? +} + +class CommonBannerView: UIView, UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout { + + enum MediaType { + case videoUrl,imageUrl,imageLocal,videoLocal + } + + private lazy var collectionView:UICollectionView = { + var layout = UICollectionViewFlowLayout() + layout.minimumLineSpacing = 0 + layout.minimumInteritemSpacing = 0 + layout.scrollDirection = .horizontal + layout.sectionInset = .zero + layout.headerReferenceSize = .zero + layout.footerReferenceSize = .zero + layout.itemSize = CGSize(width: self.width, height: self.height) + let collectionView = UICollectionView(frame:CGRect(x: 0, y: 0, width: self.width, height: self.height), collectionViewLayout: layout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.isPagingEnabled = true + collectionView.showsHorizontalScrollIndicator = false + collectionView.register(CommonBannerViewCell.self, forCellWithReuseIdentifier: "BannerView") + collectionView.decelerationRate = .normal + collectionView.contentInsetAdjustmentBehavior = .never + collectionView.backgroundColor = .white + collectionView.bounces = false + return collectionView + }() + + private lazy var pageControl:UIPageControl = { + let control = UIPageControl() + control.currentPageIndicatorTintColor = .white + control.pageIndicatorTintColor = .gray.withAlphaComponent(0.6) + return control + }() + + private var timer:Timer? + + private var items = [CommonBannerModel]() + private var selectClouse:((CommonBannerModel)->Void)? + private var autoRoll:Bool = true + private var currentPage:Int = 0 + private var timeInterval:Int = 5 + + override func awakeFromNib() { + super.awakeFromNib() + setUI() + } + + public func setItems(items:[CommonBannerModel],autoRoll:Bool = true,selectClouse:((CommonBannerModel)->Void)? = nil){ + + self.items = items + if items.count > 1{ + self.items.append(items.first!) + } + self.autoRoll = autoRoll + self.selectClouse = selectClouse + if items.count <= 1{self.autoRoll = false} + + setUI() + collectionView.reloadData() + + if self.autoRoll{ + DispatchQueue.main.asyncAfter(deadline: .now()+5) { + self.startTimer() + } + } + } + + private func setUI(){ + addSubview(collectionView) + collectionView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + if items.count > 1{ + pageControl.numberOfPages = items.count - 1 + + addSubview(pageControl) + pageControl.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().offset(-10) + make.height.equalTo(8) + } + } + } + + private func startTimer(){ + timer = Timer(timeInterval: TimeInterval(timeInterval), repeats: true, block: {[weak self] t in + guard let weakSelf = self else { return } + + var page = weakSelf.collectionView.contentOffset.x / weakSelf.collectionView.width + weakSelf.currentPage = Int(page + 1) + + if weakSelf.currentPage >= weakSelf.pageControl.numberOfPages{ + weakSelf.currentPage = 0 + weakSelf.collectionView.setContentOffset(.zero, animated: false) + weakSelf.pageControl.currentPage = 0 + } + weakSelf.collectionView.setContentOffset(CGPoint(x: weakSelf.currentPage * Int(weakSelf.width), y: 0), animated: true) + }) + timer?.fire() + RunLoop.current.add(timer!, forMode: .common) + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return items.count + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let item = items[indexPath.row] + selectClouse?(item) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width:collectionView.bounds.size.width,height:ceil(collectionView.bounds.size.height)) + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + print("--开始滑动") + timer?.fireDate = Date.distantFuture + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + let page = scrollView.contentOffset.x / scrollView.width + if page.int >= pageControl.numberOfPages{ + pageControl.currentPage = 0 + }else{ + pageControl.currentPage = page.int + } + print("--结束滑动") + timer?.fireDate = Date.init(timeIntervalSinceNow: 3.0) //3秒后开启 + } + + func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { + let page = scrollView.contentOffset.x / scrollView.width + if page.int >= pageControl.numberOfPages{ + pageControl.currentPage = 0 + }else{ + pageControl.currentPage = page.int + } + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + let page = scrollView.contentOffset.x / scrollView.width + if page.int >= pageControl.numberOfPages{ + pageControl.currentPage = 0 + collectionView.setContentOffset(.zero, animated: false) + }else{ + pageControl.currentPage = page.int + } + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BannerView", for: indexPath) as! CommonBannerViewCell + let item = items[indexPath.row] + + switch item.mediaType { + case .imageUrl: + if let i = item.resource{ + cell.imgView.sd_setImage(with: URL(string: i)) + } + case .imageLocal: + if let i = item.resource{ + cell.imgView.image = UIImage(named: i) + } + default:break + } + return cell + } +} + +class CommonBannerViewCell: UICollectionViewCell { + + lazy var imgView:UIImageView = { + let img = UIImageView() + img.contentMode = .scaleToFill + return img + }() + + override init(frame: CGRect) { + super.init(frame: frame) + contentView.addSubview(imgView) + imgView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/DolphinEnglishLearnStudent/SceneDelegate.swift b/DolphinEnglishLearnStudent/SceneDelegate.swift index eb38bf5..c32eecd 100644 --- a/DolphinEnglishLearnStudent/SceneDelegate.swift +++ b/DolphinEnglishLearnStudent/SceneDelegate.swift @@ -24,6 +24,10 @@ SVProgressHUD.setMaximumDismissTimeInterval(1.5) SVProgressHUD.setDefaultMaskType(.custom) + guard let tokenModel = LoginTokenModel.getToken(),!LoginTokenModel.isOverdue() else { + needLogin() + return + } loginSuccess() } diff --git a/DolphinEnglishLearnStudent/Services/NetworkRequest.swift b/DolphinEnglishLearnStudent/Services/NetworkRequest.swift new file mode 100644 index 0000000..7ad8825 --- /dev/null +++ b/DolphinEnglishLearnStudent/Services/NetworkRequest.swift @@ -0,0 +1,278 @@ +// +// NetworkRequest.swift +// HandyJSON +// +// Created by Sweet on 2018/12/25. +// Copyright © 2018 Sweet. All rights reserved. +// + +import Foundation +import SwifterSwift +import Alamofire +import HandyJSON +import RxSwift +import SVProgressHUD +import JQTools + +// 假设这是服务端返回的统一定义的response格式 +struct BaseResponse<T :HandyJSON>: HandyJSON { + var sysTime: Int = 0 + var code: Int = -1 // 服务端返回码 + var data: T? = nil // 具体的data的格式和业务相关,故用泛型定义 + var msg: String = "" +} + +struct BaseData<T: HandyJSON>: HandyJSON { + var records = [T]() +} + +struct SimpleModel: HandyJSON { + +} +struct HtmlModel: HandyJSON { + var content = "" + var content1 = "" + var id = 0 + var type = 0 +} + +extension String: HandyJSON{ + +} +extension Array: HandyJSON{ + +} +extension Bool: HandyJSON{ + +} + +extension Int: HandyJSON{ + +} + +let SHAKEY = "" + +class ParamsAppender: NSObject { + var url: URL + var params:Dictionary = [String: Any]() + + private init(url: String){ + self.url = URL(string: url)! + } + + @discardableResult + func interface(url: String) -> ParamsAppender { + self.url.appendPathComponent(url) + return self + } + + @discardableResult + func append(key: String,value: Bool) -> ParamsAppender { + params += ["\(key)":"\(value)"] + return self + } + + @discardableResult + func append(key: String,value: String?) -> ParamsAppender { + if value != nil && value?.isEmpty == false { + params += ["\(key)":"\(value!)"] + } + return self + } + + @discardableResult + func append(key: String,value: Array<String>) -> ParamsAppender { + if value.isEmpty == false { + params += ["\(key)":value] + } + return self + } + + + @discardableResult + func append(key: String, value: Int?) -> ParamsAppender { + if value != nil{ + params += ["\(key)":value!] + } + return self + } + + @discardableResult + func append(key: String, value: Int64) -> ParamsAppender { + params += ["\(key)":value] + return self + } + + @discardableResult + func append(key: String, value: Double?) -> ParamsAppender { + if value != nil{ + params += ["\(key)":value!] + } + return self + } + + @discardableResult + func append(key: String,data: Data?) -> ParamsAppender { + if data != nil{ + params += ["\(key)": data!] + } + return self + } + + @discardableResult + func append(key: String,url: URL) -> ParamsAppender { + params += ["\(key)":"\(url)"] + return self + } + + @discardableResult + func append(dic: [String : Any]) -> ParamsAppender { + params += dic + return self + } + + /// 参数加密 + @discardableResult + func done() -> Parameters { + var paramsArray: [String] = [] + // 排序 + let sortedArray: [String] = Array(params.keys).sorted() + + //防止自签名而错误 + if !sortedArray.contains("sign"){ + for item in sortedArray{ + // 拼接字符串 + if params.has(key: item){ + paramsArray.append("\(item)=\(params[item]!)") + } + } + let content = paramsArray.joined(separator: "&") + params += ["sign": "\(content.jq_hmacBase64(algorithm: .SHA1, key: SHAKEY))"] + +#if DEBUG + LogInfo("签名:\(content) ----- \(content.jq_hmacBase64(algorithm: .SHA1, key: SHAKEY))") +#endif + } + return self.params + } + + class func build(url: String) -> ParamsAppender { + return ParamsAppender(url: url) + } + +} +class NetworkRequest { + + static let sharedSessionManager: Alamofire.Session = { + let configuration = URLSessionConfiguration.default + configuration.timeoutIntervalForRequest = 10 + return Alamofire.Session(configuration: configuration) + }() + enum NetRequestError: Error { + case Other(Int,String) + case URLNotFound + case DownloadFailed + case InvaildSession + case ModelError(String) + case DataAnalysis(String) + } + + class func request<T: HandyJSON>(params: ParamsAppender, method: HTTPMethod, encoding: ParameterEncoding? = nil, progress: Bool = true,ignoreAlert:Bool = false) -> Observable<BaseResponse<T>>{ + + return Observable<BaseResponse<T>>.create{ ob in + guard NetworkReachabilityManager.init(host: All_Url)!.isReachable else { + alertError(msg: "当前网络不可用") + ob.onError(AFError.invalidURL(url: params.url)) + return Disposables.create{} + } + + if progress {showHUD()} + + var headers = HTTPHeaders() + + if let token = LoginTokenModel.getToken()?.access_token{ + headers.add(name: "Authorization", value: "Bearer" + " " + token) + LogInfo("USER_token:Bearer \(token)") + } + + if encoding is JSONEncoding { + headers.add(name: "Content-Type", value: "application/json;charset=UTF-8") + } + + var newEncoding: ParameterEncoding + if encoding != nil { + newEncoding = encoding! + } else { + newEncoding = method == .post ? URLEncoding.httpBody : URLEncoding.queryString + } + + sharedSessionManager.request(params.url.absoluteString, method: method, parameters:params.done(), encoding: newEncoding, headers:headers).validate().responseData{response in + LogInfo("请求地址:\(params.url)") + LogInfo("请求参数:\(params.params)") + hiddenHUD() + + guard response.error == nil else { + LogError("\(response.error!)") + + var errorString = "" + errorString.append("服务器故障:\(response.error!.localizedDescription)") + if let code = response.error?.responseCode{ + errorString.append("\n【错误码:\(code)】") + } + if !ignoreAlert{ + alert(msg: errorString) + } + ob.onError(response.error!) + return + } + if let data = response.data,let jsonString = String(data: data, encoding: String.Encoding.utf8){ + LogResponse(try! JSONSerialization.jsonObject(with: data)) + if let next = BaseResponse<T>.deserialize(from: jsonString){ + switch next.code{ + case 200:ob.onNext(next) + case 504: //登录设备最大限制 + let attribute = AttributedStringbuilder.build().add(string:next.msg, withFont: .systemFont(ofSize: 14, weight: .medium), withColor: .black.withAlphaComponent(0.6)).mutableAttributedString +// CommonAlertView.show(title: "提示", attribute: attribute, cancelStr: "关闭", completeStr: "关闭", isSingle: true, customBtnWidth: JQ_ScreenW - 170) { _ in +// +// } + case 503: //登录被冻结 + let attribute = AttributedStringbuilder.build().add(string:next.msg, withFont: .systemFont(ofSize: 14, weight: .medium), withColor: .black.withAlphaComponent(0.6)).mutableAttributedString +// CommonAlertView.show(title: "提示", attribute: attribute, cancelStr: "关闭", completeStr: "关闭", isSingle: true, customBtnWidth: JQ_ScreenW - 170) { _ in +// +// } +// case 501: +// CommonAlertView.show(title: "提示", content: next.msg,isSingle: true) { _ in +// +// } +// ob.onError(NetRequestError.InvaildSession) + case 401: + if !ignoreAlert{ + alertError(msg: "登录失效,请重新登录");ob.onError(NetRequestError.InvaildSession) + } + sceneDelegate?.needLogin() + default: + if !ignoreAlert{ + alertError(msg: "\(next.msg)") + } + ob.onError(NetRequestError.Other(next.code,next.msg)) + } + } + } + ob.onCompleted() + } + return Disposables.create{} + } + } +} +extension Dictionary { + mutating func append(dict: Dictionary) { + dict.forEach { (key, value) in + self.updateValue(value, forKey: key) + } + } +} + + +func createError(text:String,code:Int)->AFError{ + return AFError.createURLRequestFailed(error: NSError(domain: text, code: code)) +} diff --git a/DolphinEnglishLearnStudent/Services/Services.swift b/DolphinEnglishLearnStudent/Services/Services.swift new file mode 100644 index 0000000..ea96788 --- /dev/null +++ b/DolphinEnglishLearnStudent/Services/Services.swift @@ -0,0 +1,213 @@ +// +// Services.swift +// YixiuShop +// +// Created by Sweet on 2019/9/30. +// Copyright © 2019 jackLove. All rights reserved. +// + +import UIKit +import RxSwift +import Alamofire +import JQTools + +#if DEBUG +let All_Url = "http://192.168.110.237:9000" +#else +let All_Url = "http://192.168.110.237:9000" +#endif + +class Services: NSObject { + +} + +extension Services{ + class func weekList(quarter:Int)->Observable<BaseResponse<[ListenWeekModel]>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/study/base/study/weekList") + params.append(key: "quarter", value: quarter) + return NetworkRequest.request(params: params, method: .get, progress: false) + } +} + +// MARK: -- 登录部分 +extension Services{ + class func sendPhoneCode(phone:String)->Observable<BaseResponse<SimpleModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/study/base/user/sendPhoneCode") + params.append(key: "phone", value: phone) + return NetworkRequest.request(params: params, method: .get, progress: true) + } + + class func login(phone:String,code:String)->Observable<BaseResponse<LoginModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/study/base/user/studyLogin") + params.append(key: "phone", value: phone) + params.append(key: "phoneCode", value: code) + return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true) + } +} + +// MARK: -- 首页 +extension Services{ + +} + + +// MARK: -- 商品 +extension Services{ + class func goodRecommend()->Observable<BaseResponse<[RecommendModel]>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/study/base/study/goodRecommend") + return NetworkRequest.request(params: params, method: .get, progress: false) + } + + class func goodsList(keywords:String,page:Int,pageSize:Int = 20,type:[String])->Observable<BaseResponse<BaseResponseList<MarketModel>>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/goodListStudy") + .append(key: "keywords", value: keywords) + .append(key: "pageNumber", value: page) + .append(key: "pageSize", value: pageSize) + .append(key: "type", value: type) + return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: false) + } + + class func getIntegral()->Observable<BaseResponse<Int>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/study/base/study/getIntegralStudy") + return NetworkRequest.request(params: params, method: .get, progress: false) + } + + class func goodTypeStudy()->Observable<BaseResponse<[MarketTypeModel]>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/goodTypeStudy") + return NetworkRequest.request(params: params, method: .get, progress: false) + } + + class func goodsDetail(goodsId:Int)->Observable<BaseResponse<MarketDetailModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/goodDetail") + .append(key: "goodId", value: goodsId) + return NetworkRequest.request(params: params, method: .get, progress: true) + } + + class func addressList()->Observable<BaseResponse<[AddressModel]>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/shopAddress") + return NetworkRequest.request(params: params, method: .get, progress: false) + } + + class func deleteAddress(id:Int)->Observable<BaseResponse<SimpleModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/addressDelete") + .append(key: "id", value: id) + return NetworkRequest.request(params: params, method: .get, progress: true) + } + + class func redeemNow(goodId:Int)->Observable<BaseResponse<MarketDetailModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/redeemNow") + .append(key: "goodId", value: goodId) + return NetworkRequest.request(params: params, method: .get, progress: true) + } + + class func addressTree()->Observable<BaseResponse<[AddressTreeModel]>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/addressTree") + return NetworkRequest.request(params: params, method: .get, progress: true) + } + + class func addressSaveOrUpdate(id:Int?,province:AddressTreeModel?,city:AddressTreeModel?,country:AddressTreeModel?,userName:String?,userPhone:String?,isDefault:Bool,detailAddress:String)->Observable<BaseResponse<SimpleModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/addressSaveOrUpdate") + .append(key: "id", value: id) + .append(key: "address", value: detailAddress) + .append(key: "city", value: city?.name) + .append(key: "cityCode", value: city?.code) + .append(key: "province", value: province?.name) + .append(key: "provinceCode", value: province?.code) + .append(key: "recipient", value: userName) + .append(key: "recipientPhone", value: userPhone) + .append(key: "isDefault", value: (isDefault == true) ? 1:0) + return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true) + } + + class func setDefaultStudy(id:Int)->Observable<BaseResponse<SimpleModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/setDefaultStudy") + .append(key: "id", value: id) + return NetworkRequest.request(params: params, method: .get, progress: true) + } + + class func goodsExchangeStudy(goodsId:Int,number:Int,orderNumber:String,recipientId:Int,remark:String)->Observable<BaseResponse<SimpleModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/goodExchangeStudy") + .append(key: "goodId", value: goodsId) + .append(key: "number", value: number) + .append(key: "orderNumber", value: orderNumber) + .append(key: "recipientId", value: recipientId) + .append(key: "remark", value: remark) + return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true) + } + + class func userInfo()->Observable<BaseResponse<UserInfoModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/study/base/user/userInfo") + return NetworkRequest.request(params: params, method: .get, progress: false) + } + + class func integralDetail(pageNum:Int,pageSize:Int = 20,time:String?)->Observable<BaseResponse<BaseResponseList<IntegralModel>>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/study/base/study/integralDetail") + .append(key: "pageNum", value: pageNum) + .append(key: "pageSize", value: pageSize) + .append(key: "time", value: time) + return NetworkRequest.request(params: params, method: .get, progress: false) + } + + class func exchangeRecord()->Observable<BaseResponse<[ExchangeRecordModel]>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/goods/base/goods/exchangeRecord") + return NetworkRequest.request(params: params, method: .get, progress: false) + } + + class func studyGamesRecord()->Observable<BaseResponse<StudyGamesModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/study/base/study/record") + return NetworkRequest.request(params: params, method: .get, progress: true) + } + + class func logoutStudy()->Observable<BaseResponse<SimpleModel>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/study/base/user/logoutStudy") + return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true) + } + +} + +extension Services{ + class func getAgreement(type:AgreementType)->Observable<BaseResponse<String>>{ + let params = ParamsAppender.build(url: All_Url) + params.interface(url: "/study/base/user/getProtocol") + params.append(key: "type", value: type.rawValue) + return NetworkRequest.request(params: params, method: .post, progress: true) + } +} + + +extension Services{ + static func startNetworkMonitor(){ + let manager = NetworkReachabilityManager(host: All_Url) + manager?.startListening(onUpdatePerforming: { status in + switch status { + case .notReachable:alertError(msg: "当前网络不可用") + case .reachable(let type): + switch type{ + case .ethernetOrWiFi:alert(msg: "当前为Wi-Fi网络") + case .cellular:alert(msg: "当前为移动网络") + } + default:break + } + }) + } +} diff --git a/DolphinEnglishLearnStudent/ViewModel/RefreshModel.swift b/DolphinEnglishLearnStudent/ViewModel/RefreshModel.swift new file mode 100644 index 0000000..02276d9 --- /dev/null +++ b/DolphinEnglishLearnStudent/ViewModel/RefreshModel.swift @@ -0,0 +1,305 @@ +// +// RefreshModel.swift +// WanPai +// +// Created by 无故事王国 on 2023/7/11. +// + +import UIKit +import MJRefresh +import RxSwift +import HandyJSON +import RxRelay + + +enum RefreshState { + case refreshing + case completedRefresh + case moreLoading + case completedLoad + case completedLoadWithNoMoreData +} + +struct BaseResponseList<T :HandyJSON>: HandyJSON { + var records: [T] = [] + var current:Int = 0 + var hasNextPage:Bool = false + var hasPrevPage:Bool = false + var pages:Int = 0 + var size:Int = 0 + var startIndex:Int = 0 + var total:Int = 0 +} + +protocol RefreshModelProctol { + associatedtype T:HandyJSON + func api()->(Observable<BaseResponse<[T]>>)? +} + +protocol RefreshModelInnerProctol { + associatedtype T:HandyJSON + func api()->(Observable<BaseResponse<BaseResponseList<T>>>)? +} + + +class RefreshModel<T:HandyJSON>:RefreshModelProctol{ + func api() -> (RxSwift.Observable<BaseResponse<[T]>>)? {return nil} + let disposeBag = DisposeBag() + enum RefreshType {case refresh,load} + + private var handle:UIScrollView! + + lazy var refreshSubject = PublishSubject<RefreshState>() + var page:Int = 1 + var pageSize:Int = 20 + private var needRefreshData:Bool = true + lazy var dataSource = BehaviorRelay<[T]>(value: []) + + func resetPage(){ + page = 1 + } + + func configure(_ scrollView:UITableView,needMore:Bool = true){ + scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) + if needMore{ + scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) + } + refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) + handle = scrollView + } + + func configure(_ scrollView:UICollectionView,needMore:Bool = true,needRefreshData:Bool = true){ + self.needRefreshData = needRefreshData + scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) + if needMore{ + scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) + } + refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) + handle = scrollView + } + + func beginRefresh(){ + guard handle != nil else {return} + api()?.subscribe(onNext: { data in + if let datas = data.data{ + self.dataSource.accept(datas) + self.refreshSubject.onNext(.completedRefresh) + } + }).disposed(by: disposeBag) + } + + func refreshData() ->(()->Void) { + return {self.request(status: .refresh)} + } + + func loadMoreData() ->(()->Void) { + return {self.request(status: .load)} + } + + func request(status:RefreshType){ + switch status { + case .refresh: + self.page = 1 + self.refreshSubject.onNext(.refreshing) + case .load: + self.page += 1 + self.refreshSubject.onNext(.moreLoading) + } + + api()?.subscribe(onNext: { data in + if let datas = data.data{ + switch status{ + case .refresh: + self.dataSource.accept(datas) + self.refreshSubject.onNext(.completedRefresh) + case .load: + self.dataSource.accept(self.dataSource.value + datas) + if datas.count == 0{ + self.refreshSubject.onNext(.completedLoadWithNoMoreData) + }else{ + self.refreshSubject.onNext(.completedLoad) + } + } + }else{ + self.refreshSubject.onNext(.completedLoadWithNoMoreData) + } + }, onError: { error in + self.refreshSubject.onNext(.completedLoad) + + }).disposed(by: disposeBag) + } +} + + +class RefreshInnerModel<T:HandyJSON>:RefreshModelInnerProctol{ + func api() -> (RxSwift.Observable<BaseResponse<BaseResponseList<T>>>)? { + return nil + } + + let disposeBag = DisposeBag() + enum RefreshType {case refresh,load} + + private var handle:UIScrollView! + + lazy var refreshSubject = PublishSubject<RefreshState>() + var page:Int = 0 + var pageSize:Int = 20 + private var needRefreshData:Bool = true + lazy var dataSource = BehaviorRelay<BaseResponseList<T>?>(value: nil) + + func configure(_ scrollView:UITableView,needMore:Bool = true){ + scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) + if needMore{ + scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) + } + refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) + handle = scrollView + } + + func configure(_ scrollView:UICollectionView,needMore:Bool = true,needRefreshData:Bool = true){ + self.needRefreshData = needRefreshData + scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) + if needMore{ + scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) + } + refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) + handle = scrollView + } + func resetPage(){ + page = 1 + } + + func beginRefresh(){ + handle.mj_header?.beginRefreshing() + } + + func refreshData() ->(()->Void) { + return {self.request(status: .refresh)} + } + + func loadMoreData() ->(()->Void) { + return {self.request(status: .load)} + } + + func request(status:RefreshType){ + switch status { + case .refresh: + self.page = 1 + self.refreshSubject.onNext(.refreshing) + case .load: + self.page += 1 + self.refreshSubject.onNext(.moreLoading) + } + + api()?.subscribe(onNext: { data in + switch status{ + case .refresh: + self.dataSource.accept(data.data) + self.refreshSubject.onNext(.completedRefresh) + case .load: + var new = self.dataSource.value?.records ?? [] + new.append(contentsOf: data.data?.records ?? []) + var model = self.dataSource.value + model!.records = new + self.dataSource.accept(model) + if data.data?.records.count == 0{ + self.refreshSubject.onNext(.completedLoadWithNoMoreData) + }else{ + self.refreshSubject.onNext(.completedLoad) + } + } + }, onError: { error in + self.refreshSubject.onNext(.completedLoad) + + }).disposed(by: disposeBag) + } + + +} + + +class CustomRefreshHeaer:MJRefreshNormalHeader{ + static func refreshing(with refreshingBlock: @escaping MJRefreshComponentAction) -> MJRefreshNormalHeader? { + let refreshHeader = MJRefreshNormalHeader(refreshingBlock: refreshingBlock) + return refreshHeader + } +} + +class CustomRefreshFooter:MJRefreshAutoNormalFooter{ + static func refreshing(with refreshingBlock: @escaping MJRefreshComponentAction) -> MJRefreshAutoNormalFooter? { + let refrehFooter = MJRefreshAutoNormalFooter(refreshingBlock: refreshingBlock) + refrehFooter.isRefreshingTitleHidden = true + refrehFooter.stateLabel?.isHidden = true + return refrehFooter + } +} + +extension Reactive where Base : UITableView { + + func handlestatus() -> Binder<RefreshState> { + return Binder(self.base) { (tableView, status) in + switch status { + case .moreLoading: + self.base.mj_footer?.beginRefreshing() + case .refreshing: + self.base.reloadData() + self.base.mj_footer?.resetNoMoreData() + self.base.mj_header?.beginRefreshing() + + case .completedLoadWithNoMoreData: + DispatchQueue.main.async { + self.base.reloadData() + } + (self.base.mj_footer as? MJRefreshAutoNormalFooter)?.stateLabel?.isHidden = false + self.base.mj_footer?.endRefreshingWithNoMoreData() + self.base.mj_header?.endRefreshing() + case .completedLoad: + DispatchQueue.main.async { + self.base.reloadData() + } + self.base.mj_footer?.endRefreshing() + self.base.mj_header?.endRefreshing() + default: + DispatchQueue.main.async { + self.base.reloadData() + } + self.base.mj_header?.endRefreshing() + self.base.mj_footer?.endRefreshing() + } + } + } +} + +extension Reactive where Base : UICollectionView { + + func handlestatus() -> Binder<RefreshState> { + return Binder(self.base) { (tableView, status) in + switch status { + case .moreLoading: + self.base.mj_footer?.beginRefreshing() + case .refreshing: + self.base.reloadData() + self.base.mj_footer?.resetNoMoreData() + self.base.mj_header?.endRefreshing() + + case .completedLoadWithNoMoreData: + DispatchQueue.main.async { + self.base.reloadData() + } + self.base.mj_footer?.endRefreshingWithNoMoreData() + case .completedLoad: + DispatchQueue.main.async { + self.base.reloadData() + } + self.base.mj_footer?.endRefreshing() + default: + DispatchQueue.main.async { + self.base.reloadData() + } + self.base.mj_header?.endRefreshing() + self.base.mj_footer?.endRefreshing() + } + } + } +} + diff --git a/DolphinEnglishLearnStudent/ViewModel/UserViewModel.swift b/DolphinEnglishLearnStudent/ViewModel/UserViewModel.swift new file mode 100644 index 0000000..bb2051a --- /dev/null +++ b/DolphinEnglishLearnStudent/ViewModel/UserViewModel.swift @@ -0,0 +1,55 @@ +// +// UserViewModel.swift +// DolphinEnglishLearnStudent +// +// Created by 无故事王国 on 2024/5/30. +// + +import Foundation +import UserDefaultsStore +import HandyJSON + +struct UserInfoModel:HandyJSON,Identifiable,Codable{ + static let idKey = \UserInfoModel.id + var id: Int = 0 + var account: String = "" + var birthday: String = "" + var createBy: String = "" + var createTime: String = "" + var disabled: Bool = false + var gender: Int = 0 + var headImg: String = "" + var insertTime: String = "" + var integral: Int = 0 + var isVip: Int = 0 + var name: String = "" + var openId: String = "" + var password: String = "" + var phone: String = "" + var state: Int = 0 + var updateBy: String = "" + var updateTime: String = "" + var vipEndTime: String = "" + var vipPayTime: String = "" + +} + +class UserViewModel{ + private static let userInfo = UserDefaultsStore<UserInfoModel>(uniqueIdentifier: "UserInfoModel")! + + static func saveUserInfo(_ model:UserInfoModel){ + do{ + try UserViewModel.userInfo.save(model) + }catch{ + + } + } + + static func getUserInfo()->UserInfoModel?{ + return UserViewModel.userInfo.allObjects().first + } + + static func clearUserInfo(){ + UserViewModel.userInfo.deleteAll() + } +} diff --git a/Podfile b/Podfile index 35aa517..08b97b2 100644 --- a/Podfile +++ b/Podfile @@ -13,5 +13,6 @@ # pod 'Alamofire' # 网络请求框架 pod 'SVProgressHUD' # 提示框组件 pod 'CryptoSwift' # 常用加密算法 + pod 'Alamofire' # 网络请求框架 end -- Gitblit v1.7.1