From 6eba31963b893bd0fc8b02768fee8e24d20f35ad Mon Sep 17 00:00:00 2001 From: linshujie Date: Tue, 7 Jul 2026 18:49:11 +0800 Subject: [PATCH] =?UTF-8?q?bug=E4=BF=AE=E6=94=B9=20=20=20=20=20=20=20-=20?= =?UTF-8?q?=20=E5=BE=AE=E4=BF=A1=E7=99=BB=E5=BD=95=E9=A6=96=E6=AC=A1?= =?UTF-8?q?=E6=B2=A1=E5=8F=8D=E5=BA=94=20=20=20=20=20=20=20-=20=20UI?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/logo108.imageset/Contents.json | 21 ++++ .../Common/logo108.imageset/logo108@2x.png | Bin 0 -> 11372 bytes QuickLocation/Common/Constant.swift | 8 +- .../BaseViewController.swift | 32 +---- .../Main/Tabbar/MainTabBarController.swift | 3 +- .../Manager/Account/UserConfigModel.swift | 28 +++++ QuickLocation/Manager/App/ApiManager.swift | 1 + .../Group/InviteJoin/InviteJoinVC.swift | 3 +- QuickLocation/Section/Home/HomeView.swift | 2 + .../Section/Home/HomeViewController.swift | 117 ++++++++++++------ .../Section/Home/InteractionView.swift | 54 +++++--- .../Section/Launch/LaunchViewController.swift | 25 ++-- QuickLocation/Section/Login/LoginView.swift | 33 +++-- .../Section/Login/LoginViewController.swift | 77 ++++++++++-- .../Section/Login/LoginViewModel.swift | 108 ++++++++++++---- .../Section/Login/OneTapLoginView.swift | 30 +++-- .../Section/Mine/Account/AccountVC.swift | 1 + QuickLocation/Section/Mine/MineView.swift | 36 +++++- .../PopupWindow/PopupQueueManager.swift | 65 +++++++++- .../Section/PopupWindow/PopupWindow.swift | 28 ++++- .../PopupWindow/PromotionalActivitiesVC.swift | 53 ++++---- .../PromotionalActivitiesView.swift | 2 +- .../Section/Schedule/ScheduleVC.swift | 10 ++ .../Section/Schedule/ScheduleView.swift | 27 ++++ .../Section/Share/SharePopView.swift | 61 ++++++--- .../Section/VipRecharge/VipRechargeVC.swift | 51 ++++---- .../Section/VipRecharge/VipRechargeView.swift | 4 +- 27 files changed, 653 insertions(+), 227 deletions(-) create mode 100644 QuickLocation/Assets.xcassets/Common/logo108.imageset/Contents.json create mode 100644 QuickLocation/Assets.xcassets/Common/logo108.imageset/logo108@2x.png diff --git a/QuickLocation/Assets.xcassets/Common/logo108.imageset/Contents.json b/QuickLocation/Assets.xcassets/Common/logo108.imageset/Contents.json new file mode 100644 index 0000000..e2c7866 --- /dev/null +++ b/QuickLocation/Assets.xcassets/Common/logo108.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "logo108@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/QuickLocation/Assets.xcassets/Common/logo108.imageset/logo108@2x.png b/QuickLocation/Assets.xcassets/Common/logo108.imageset/logo108@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2422b180e282c6f9b418a51152180f4862bcdd45 GIT binary patch literal 11372 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 uY7$ { .init("isReLogin", defaultValue: false) } /// 显示引导页 - var guideShowVersion: DefaultsKey { .init("guideShowVersion") } + var isShowGuide: DefaultsKey { .init("isShowGuide", defaultValue: true) } } /// 通知常量 @@ -35,6 +35,12 @@ extension Notification.Name { static let ShowMemberLocationNotification = Notification.Name("ShowMemberLocationNotification") /// 支付宝/微信支付结果回调 static let RequestOrderPayStatusNotification = Notification.Name("RequestOrderPayStatusNotification") + /// 暂停弹窗队列(第三方登录/注册跳转时) + static let pausePopupQueue = Notification.Name("pausePopupQueue") + /// 恢复弹窗队列(第三方登录取消/失败时) + static let resumePopupQueue = Notification.Name("resumePopupQueue") + /// 终止弹窗队列(登录成功后) + static let invalidatePopupQueue = Notification.Name("invalidatePopupQueue") } diff --git a/QuickLocation/Main/BaseViewController/BaseViewController.swift b/QuickLocation/Main/BaseViewController/BaseViewController.swift index 929ce95..0aa6d36 100644 --- a/QuickLocation/Main/BaseViewController/BaseViewController.swift +++ b/QuickLocation/Main/BaseViewController/BaseViewController.swift @@ -33,8 +33,7 @@ class BaseViewController: UIViewController { /// 导航栏标题 var navTitle: String = "" { didSet { - backButton.setTitle(" \(navTitle)", for: .normal) -// navigationItem.title = navTitle + navigationItem.title = navTitle } } @@ -102,32 +101,13 @@ class BaseViewController: UIViewController { dismissItem.tintColor = .white navigationItem.leftBarButtonItem = dismissItem } else if let count = navigationController?.viewControllers.count, count > 1 { - let leftBarBtn = UIBarButtonItem (customView: backButton) - - //用于消除左边空隙,要不然按钮顶不到最前面 - let spacer = UIBarButtonItem (barButtonSystemItem: .fixedSpace, target: nil, action: nil) - spacer.width = -10 - navigationItem.leftBarButtonItem = leftBarBtn -// navigationItem.leftBarButtonItems = [spacer, leftBarBtn] -// let leftBackItem = UIBarButtonItem(image: UIImage(named: "Common/back"), -// style: .plain, -// target: self, action: #selector(leftBackClicked(_:))) -// leftBackItem.tintColor = .white -// navigationItem.leftBarButtonItem = leftBackItem + let leftBackItem = UIBarButtonItem(image: UIImage(named: "Common/back"), + style: .plain, + target: self, + action: #selector(leftBackClicked(_:))) + navigationItem.leftBarButtonItem = leftBackItem } } - - lazy var backButton: UIButton = { - let button = UIButton (type: .custom) - button.frame = CGRectMake (0, 0, 200, 30) - button.setImage(UIImage(named: "Common/back"), for: .normal) - button.titleLabel?.font = .systemFont(ofSize: 18, weight: .medium) - button.contentHorizontalAlignment = .left - button.setTitleColor(ThemeManager.shared.color.titleColor, for: .normal) - button.addTarget(self, action: #selector(leftBackClicked(_:)), for: .touchUpInside) - return button - }() - } // MARK: - 验证码 diff --git a/QuickLocation/Main/Tabbar/MainTabBarController.swift b/QuickLocation/Main/Tabbar/MainTabBarController.swift index e145772..03ed688 100644 --- a/QuickLocation/Main/Tabbar/MainTabBarController.swift +++ b/QuickLocation/Main/Tabbar/MainTabBarController.swift @@ -50,6 +50,7 @@ final class MainTabBarController: UITabBarController { viewControllers = navControllers tabBar.isHidden = true + tabBar.removeFromSuperview() for nav in navControllers { nav.delegate = self @@ -71,7 +72,7 @@ final class MainTabBarController: UITabBarController { NSLayoutConstraint.activate([ customTabBar.leadingAnchor.constraint(equalTo: view.leadingAnchor), customTabBar.trailingAnchor.constraint(equalTo: view.trailingAnchor), - customTabBar.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -41), + customTabBar.bottomAnchor.constraint(equalTo: view.bottomAnchor), customTabBar.heightAnchor.constraint(equalToConstant: tabBarHeight) ]) } diff --git a/QuickLocation/Manager/Account/UserConfigModel.swift b/QuickLocation/Manager/Account/UserConfigModel.swift index 9934cde..5ef2208 100644 --- a/QuickLocation/Manager/Account/UserConfigModel.swift +++ b/QuickLocation/Manager/Account/UserConfigModel.swift @@ -68,6 +68,8 @@ struct SystemConfigModel: Mappable { var popupConfig: PopupWindowConfigModel? /// 弹窗时间配置 var timeConfig: PopupWindowTimeModel? + /// 微信分享配置 + var shareConfig: WxShareConfigModel? /// 聊天界面的警告说明 var chatWarning: String = "" @@ -84,6 +86,7 @@ struct SystemConfigModel: Mappable { groupBannerList <- map["client.team.ad", nested: false] popupConfig <- map["client.popup.display", nested: false] timeConfig <- map["client.time.setting", nested: false] + shareConfig <- map["client.weixin.share", nested: false] } } @@ -132,3 +135,28 @@ struct PopupWindowTimeModel: Mappable { pay_pop_type <- map["pay_pop_type"] } } + +struct WxShareConfigModel: Mappable { + /// 链接 + var link: String = "" + + /// 标题 + var title: String = "" + + /// 内容 + var content: String = "" + + /// 图标 + var image: String = "" + + init?(map: Map) { + + } + + mutating func mapping(map: Map) { + link <- map["link"] + title <- map["title"] + content <- map["content"] + image <- map["image"] + } +} diff --git a/QuickLocation/Manager/App/ApiManager.swift b/QuickLocation/Manager/App/ApiManager.swift index 6bd0096..27871e1 100644 --- a/QuickLocation/Manager/App/ApiManager.swift +++ b/QuickLocation/Manager/App/ApiManager.swift @@ -157,6 +157,7 @@ extension ApiManager { // MARK: - 处理Token过期 private func handleTokenExpired(msg: String?) { MainAsync { + NotificationCenter.default.post(name: .invalidatePopupQueue, object: nil) AppContextManager.shared.deleteAccount() GroupIMService.shared.logout() DLToast.showError(text: msg ?? "登录失效,请重新登录!") { diff --git a/QuickLocation/Section/Group/InviteJoin/InviteJoinVC.swift b/QuickLocation/Section/Group/InviteJoin/InviteJoinVC.swift index 9ecf88c..37b96de 100644 --- a/QuickLocation/Section/Group/InviteJoin/InviteJoinVC.swift +++ b/QuickLocation/Section/Group/InviteJoin/InviteJoinVC.swift @@ -28,8 +28,7 @@ class InviteJoinVC: BaseViewController { // 分享 rootView.shareAppBtn.rx.tap.subscribe(onNext: { _ in - let url = "https://smartdrive.zuom8.cn/jisu/share/user/" - SharePopView.show(shareURL: url) + SharePopView.show() }).disposed(by: disposeBag) guard let model = groupInModel else { return } diff --git a/QuickLocation/Section/Home/HomeView.swift b/QuickLocation/Section/Home/HomeView.swift index 2cb6980..678306b 100644 --- a/QuickLocation/Section/Home/HomeView.swift +++ b/QuickLocation/Section/Home/HomeView.swift @@ -236,6 +236,7 @@ class HomeView: UIView { quickMessageView.isHidden = false interactionView.isHidden = false toolsView.isHidden = true + searchLottieView.isHidden = true quickMessageView.frame.origin.y = kScreenHeight - 384 - 93 interactionView.frame.origin.y = kScreenHeight - 384 } @@ -246,6 +247,7 @@ class HomeView: UIView { quickMessageView.isHidden = true interactionView.isHidden = true toolsView.isHidden = false + searchLottieView.isHidden = false onDismissPanel?() } diff --git a/QuickLocation/Section/Home/HomeViewController.swift b/QuickLocation/Section/Home/HomeViewController.swift index eab8424..0909c91 100644 --- a/QuickLocation/Section/Home/HomeViewController.swift +++ b/QuickLocation/Section/Home/HomeViewController.swift @@ -46,6 +46,7 @@ class HomeViewController: BaseViewController { private var subscribedMemberIds: [String] = [] /// 底部成员面板是否显示 private var isMemberPanelShown = false + private var isSOSPlaying = false /// 记录每个成员最后一次收到位置的时间(MQTT userId → Date) private var lastUpdateTimes: [String: Date] = [:] /// 离线检测定时器 @@ -73,8 +74,6 @@ class HomeViewController: BaseViewController { setupHeading() reactiveAction() - requestUserConfig() - rootView.quickMessageView.tagListView.delegate = self // MQTT 位置上报 UIDevice.current.isBatteryMonitoringEnabled = true @@ -189,9 +188,26 @@ class HomeViewController: BaseViewController { // User Config刷新 NotificationCenter.default.rx.notification(.RefreshUserConfigNotification, object: nil) - .subscribe { [weak self] notification in - self?.requestUserConfig() - }.disposed(by: disposeBag) + .startWith(Notification(name: .RefreshUserConfigNotification)) + .do(onNext: { [weak self] _ in + self?.popupQueue = nil + }) + .flatMapLatest { _ in + SystemService.userConfig() + } + .subscribe(onNext: { [weak self] response in + guard let self = self, let model = response.model else { return } + Defaults[\.loginToken] = model.token + AppContextManager.shared.systemConfig = model.config + self.getUserIMToken() + self.requestUserInfo { [weak self] in + self?.requestGroupInfo() + } + self.requestNotice() + self.popupQueue = nil + self.popupQueue = PopupQueueManager() + self.popupQueue?.start(from: self) + }).disposed(by: disposeBag) // 圈子刷新 NotificationCenter.default.rx.notification(.RefreshGroupInfoNotification, object: nil) @@ -248,10 +264,8 @@ class HomeViewController: BaseViewController { } // 分享 - rootView.interactionView.onShare = { [weak self] in - guard let self = self, let member = self.rootView.interactionView.currentMember else { return } - let url = "https://smartdrive.zuom8.cn/jisu/share/user/\(member.id)" - SharePopView.show(shareURL: url) + rootView.interactionView.onShare = { + SharePopView.show() } } @@ -290,25 +304,6 @@ class HomeViewController: BaseViewController { // MARK: - API - /// 获取用户配置 - func requestUserConfig() { - SystemService.userConfig().subscribe(onNext: { response in - guard let model = response.model else { return } - Defaults[\.loginToken] = model.token - AppContextManager.shared.systemConfig = model.config - self.getUserIMToken() - // 先更新用户信息(含头像),再拉群列表同步地图标注,避免头像旧 - self.requestUserInfo { [weak self] in - self?.requestGroupInfo() - } - // 首页公告 - self.requestNotice() - // 弹窗控制器 - self.popupQueue = PopupQueueManager() - self.popupQueue?.start(from: self) - }).disposed(by: disposeBag) - } - /// 获取用户IM Token func getUserIMToken() { DLToast.showLoading() @@ -466,8 +461,8 @@ class HomeViewController: BaseViewController { let newMembers = others + [me] members = newMembers currentUserMember = me - // 从 members 中过滤出在线成员用于地图标注 - let onlineMembers = newMembers.filter { $0.isOnline } + // 从 members 中过滤出在线成员用于地图标注(当前用户坐标无效,由 GPS 回调单独添加) + let onlineMembers = newMembers.filter { $0.isOnline && !$0.isCurrentUser } guard let mapView = rootView.mapView else { return } @@ -477,10 +472,33 @@ class HomeViewController: BaseViewController { let toRemove = existing.filter { !onlineIDs.contains($0.member.id) } mapView.removeAnnotations(toRemove) + if toRemove.contains(where: { $0 === currentUserAnnotation }) { + currentUserAnnotation = nil + } let toAdd = onlineMembers.filter { !existingIDs.contains($0.id) } let annotations = toAdd.map { MemberAnnotation(member: $0) } mapView.addAnnotations(annotations) + + if currentUserAnnotation == nil, + let me = currentUserMember, + let loc = lastLocation, + CLLocationCoordinate2DIsValid(loc.coordinate) { + let updatedMe = CircleMember( + id: "current", + name: me.name, + avatar: me.avatar, + isOnline: true, + isOwner: false, + coordinate: loc.coordinate, + address: me.address, + heading: me.heading, + lastUpdateTime: me.lastUpdateTime, + battery: me.battery + ) + currentUserMember = updatedMe + mapView.addAnnotation(MemberAnnotation(member: updatedMe)) + } #endif } @@ -639,15 +657,21 @@ extension HomeViewController { } } case "sos": // 求助 - self.requestGroupInfo(isDefaultGroup: false) guard let userId = msg.data?.user_id, userId != AppContextManager.shared.userId else { return } - playSOSAlarm() - rootView.sosPopView.isHidden = false - rootView.sosPopView.play(toFrame: 30.0) { completed in - guard completed else { return } - self.rootView.sosPopView.stop() - self.rootView.sosPopView.isHidden = true + DispatchQueue.main.async { [weak self] in + guard let self = self, !self.isSOSPlaying else { return } + self.isSOSPlaying = true + self.playSOSAlarm() + self.rootView.sosPopView.isHidden = false + self.rootView.sosPopView.play { completed in + self.rootView.sosPopView.stop() + self.rootView.sosPopView.isHidden = true + } + DispatchQueue.main.asyncAfter(deadline: .now() + 15) { [weak self] in + self?.rootView.sosPopView.stop() + self?.rootView.sosPopView.isHidden = true + } } case "join": // 圈子有成员加入 guard let gk = msg.data?.group_key, @@ -798,6 +822,22 @@ extension HomeViewController: MAMapViewDelegate { if let ann = currentUserAnnotation { ann.coordinate = coordinate + } else if let me = currentUserMember { + let updatedMe = CircleMember( + id: "current", + name: me.name, + avatar: me.avatar, + isOnline: true, + isOwner: false, + coordinate: coordinate, + address: me.address, + heading: me.heading, + lastUpdateTime: me.lastUpdateTime, + battery: me.battery + ) + currentUserMember = updatedMe + let annotation = MemberAnnotation(member: updatedMe) + mapView.addAnnotation(annotation) } if !isMemberPanelShown { @@ -835,13 +875,14 @@ extension HomeViewController { return } print("✅ SOS: playing alarm") - player.numberOfLoops = 0 + player.numberOfLoops = -1 player.volume = 1.0 player.play() self.sosPlayer = player DispatchQueue.main.asyncAfter(deadline: .now() + 15) { [weak self] in self?.sosPlayer?.stop() self?.sosPlayer = nil + self?.isSOSPlaying = false } } } diff --git a/QuickLocation/Section/Home/InteractionView.swift b/QuickLocation/Section/Home/InteractionView.swift index 92a927e..dfabac3 100644 --- a/QuickLocation/Section/Home/InteractionView.swift +++ b/QuickLocation/Section/Home/InteractionView.swift @@ -108,7 +108,10 @@ class InteractionView: UIView { let pages = (items.count + InteractionView.emojiPerPage - 1) / InteractionView.emojiPerPage self.emojiPageControl.numberOfPages = max(pages, 1) self.emojiPageControl.currentPage = 0 - self.emojiCollectionView.setContentOffset(.zero, animated: false) + self.emojiCollectionView.layoutIfNeeded() + DispatchQueue.main.async { + self.emojiCollectionView.setContentOffset(.zero, animated: false) + } }) .disposed(by: disposeBag) } @@ -517,6 +520,11 @@ extension InteractionView: UICollectionViewDelegate { emojiPageControl.currentPage = page } + func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard collectionView == emojiCollectionView else { return } + (cell as? EmojiPanelCell)?.playAnimation() + } + func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { guard collectionView == emojiCollectionView else { return } (cell as? EmojiPanelCell)?.stopAnimation() @@ -528,29 +536,46 @@ final class EmojiPanelCell: UICollectionViewCell { private static var animationCache: [String: LottieAnimation] = [:] private static var loadingCompletions: [String: [(LottieAnimation?) -> Void]] = [:] - private static let animationQueue = DispatchQueue(label: "com.quicklocation.emoji.animation", qos: .userInitiated) + private static let cacheLock = NSLock() + private static let animationQueue = DispatchQueue(label: "com.quicklocation.emoji.animation", qos: .userInitiated, attributes: .concurrent) + + private static let emojiPathMap: [String: String] = { + Bundle.main.paths(forResourcesOfType: "json", inDirectory: nil).reduce(into: [:]) { map, path in + let name = (path as NSString).lastPathComponent.replacingOccurrences(of: ".json", with: "") + map[name] = path + } + }() static func loadAnimation(for name: String, completion: @escaping (LottieAnimation?) -> Void) { animationQueue.async { - if let cached = animationCache[name] { + Self.cacheLock.lock() + if let cached = Self.animationCache[name] { + Self.cacheLock.unlock() DispatchQueue.main.async { completion(cached) } return } - if loadingCompletions[name] != nil { - loadingCompletions[name]?.append(completion) + if Self.loadingCompletions[name] != nil { + Self.loadingCompletions[name]?.append(completion) + Self.cacheLock.unlock() return } - loadingCompletions[name] = [completion] + Self.loadingCompletions[name] = [completion] + Self.cacheLock.unlock() + let animation: LottieAnimation? - if let path = Bundle.main.path(forResource: name, ofType: "json") { + if let path = Self.emojiPathMap[name] { animation = LottieAnimation.filepath(path) } else { animation = nil } - animationCache[name] = animation - let completions = loadingCompletions.removeValue(forKey: name) ?? [] + + Self.cacheLock.lock() + Self.animationCache[name] = animation + let completions = Self.loadingCompletions.removeValue(forKey: name) ?? [] + Self.cacheLock.unlock() + DispatchQueue.main.async { completions.forEach { $0(animation) } } @@ -558,7 +583,6 @@ final class EmojiPanelCell: UICollectionViewCell { } private var animationName: String? - private var shouldPlayAnimation = false private let lottieView: LottieAnimationView = { let v = LottieAnimationView() @@ -598,36 +622,28 @@ final class EmojiPanelCell: UICollectionViewCell { func configure(name: String) { animationName = name - shouldPlayAnimation = false lottieView.stop() - lottieView.animation = nil - lottieView.currentProgress = 0 Self.loadAnimation(for: name) { [weak self] animation in guard let self = self, self.animationName == name else { return } self.lottieView.animation = animation self.lottieView.currentProgress = 0 - if self.shouldPlayAnimation { - self.lottieView.play() - } + self.lottieView.play() } lockView.isHidden = AppContextManager.shared.vip > 1 } func playAnimation() { - shouldPlayAnimation = true lottieView.play() } func stopAnimation() { - shouldPlayAnimation = false lottieView.stop() } override func prepareForReuse() { super.prepareForReuse() animationName = nil - shouldPlayAnimation = false lottieView.stop() lottieView.animation = nil } diff --git a/QuickLocation/Section/Launch/LaunchViewController.swift b/QuickLocation/Section/Launch/LaunchViewController.swift index 131f6a2..e92bb5a 100644 --- a/QuickLocation/Section/Launch/LaunchViewController.swift +++ b/QuickLocation/Section/Launch/LaunchViewController.swift @@ -34,9 +34,7 @@ class LaunchViewController: BaseViewController { AppDelegate.shared.showMainViewController() }).disposed(by: disposeBag) - getUserConfig() - navigateAfterDelay() } @@ -44,24 +42,16 @@ class LaunchViewController: BaseViewController { Observable.just(()) .delay(.milliseconds(2500), scheduler: MainScheduler.instance) .subscribe(onNext: { [weak self] in - self?.navigateToNext() + if Defaults[\.isShowGuide] { + self?.collectionView.isHidden = false + self?.pageControl.isHidden = false + self?.experienceBtn.isHidden = false + } else { + AppDelegate.shared.showMainViewController() + } }) .disposed(by: disposeBag) } - - private func navigateToNext() { - let hasShownGuide = UserDefaults.standard.bool(forKey: "hasShownGuide") - if hasShownGuide { - let loginVC = LoginViewController() - UIView.transition(with: view.window!, duration: 0.4, options: .transitionCrossDissolve) { - self.view.window?.rootViewController = UINavigationController(rootViewController: loginVC) - } - } else { - collectionView.isHidden = false - pageControl.isHidden = false - experienceBtn.isHidden = false - } - } /// 获取用户配置 func getUserConfig() { @@ -82,7 +72,6 @@ class LaunchViewController: BaseViewController { /// 获取用户IM Token func getUserIMToken() { - DLToast.showLoading() UserService.imToken().subscribe(onNext: { response in guard let data = response.data, let token = data["token"] as? String else { return } AppContextManager.shared.imToken = token diff --git a/QuickLocation/Section/Login/LoginView.swift b/QuickLocation/Section/Login/LoginView.swift index 76fcae9..4c80781 100644 --- a/QuickLocation/Section/Login/LoginView.swift +++ b/QuickLocation/Section/Login/LoginView.swift @@ -68,17 +68,18 @@ class LoginView: UIView { addSubview(bgMaskImage) addSubview(backBtn) addSubview(guestLoginButton) - addSubview(welcomeLabel) - addSubview(phoneNumberLabel) - addSubview(carrierLabel) - addSubview(otherPhoneView) + addSubview(inputContainerView) + inputContainerView.addSubview(welcomeLabel) + inputContainerView.addSubview(phoneNumberLabel) + inputContainerView.addSubview(carrierLabel) + inputContainerView.addSubview(otherPhoneView) otherPhoneView.addSubview(phoneInputView) otherPhoneView.addSubview(smsCodeInputView) - addSubview(loginButton) + inputContainerView.addSubview(loginButton) addSubview(appleLoginBtn) addSubview(wechatLoginBtn) addSubview(phoneLoginBtn) - + addSubview(agreementView) agreementView.addSubview(checkBox) agreementView.addSubview(agreementTV) @@ -98,8 +99,14 @@ class LoginView: UIView { .width(85) .height(29) + inputContainerView.layoutChain + .centerX() + .centerY(self, offset: -17) + .edgesHorzontal() + .height(306) + welcomeLabel.layoutChain - .top(202 - kNaviHeight) + .top(0) .centerX() .edgesHorzontal() @@ -161,7 +168,7 @@ class LoginView: UIView { agreementView.layoutChain .leftToView(loginButton) .rightToView(loginButton) - .bottom(55) + .bottom(kSafeBottomMargin + 20) .height(30) agreementLabel.layoutChain.edges() @@ -178,7 +185,7 @@ class LoginView: UIView { .top() phoneLoginBtn.layoutChain - .bottomToTopOfView(agreementView, offset: -81) + .bottomToTopOfView(agreementView, offset: -30) .centerX() .width(38) .heightToWidth(1) @@ -258,6 +265,13 @@ class LoginView: UIView { return label }() + // 输入区域容器 + lazy var inputContainerView: UIView = { + let view = UIView() + view.backgroundColor = .clear + return view + }() + // 其他手机号登录 lazy var otherPhoneView: UIView = { let view = UIView() @@ -394,7 +408,6 @@ class LoginView: UIView { textView.backgroundColor = .clear textView.isEditable = false textView.isScrollEnabled = false - textView.isSelectable = false textView.linkTextAttributes = [:] return textView diff --git a/QuickLocation/Section/Login/LoginViewController.swift b/QuickLocation/Section/Login/LoginViewController.swift index 2ce1409..e24de4e 100644 --- a/QuickLocation/Section/Login/LoginViewController.swift +++ b/QuickLocation/Section/Login/LoginViewController.swift @@ -9,6 +9,7 @@ import UIKit import SnapKit import RxSwift import RxCocoa +import AuthenticationServices #if !targetEnvironment(simulator) import GeYanSdk #endif @@ -39,14 +40,14 @@ class LoginViewController: BaseViewController { bindViewModel() reactiveAction() } - + private func reactiveAction() { rootView.smsCodeBtn.rx.controlEvent(.touchUpInside).subscribe { [weak self] _ in guard let self = self else { return } self.requestSmsCodeAPI() }.disposed(by: rootView.disposeBag) } - + // MARK: - Bindings private func bindViewModel() { rootView.checkBox.rx.tap @@ -100,17 +101,40 @@ class LoginViewController: BaseViewController { }) .disposed(by: disposeBag) + viewModel.onAppleLoginRequest = { [weak self] controller in + guard let self = self else { return } + controller.delegate = self + controller.presentationContextProvider = self + controller.performRequests() + } + rootView.phoneLoginBtn.rx.tap .subscribe(onNext: { [weak self] in - + #if !targetEnvironment(simulator) + if GeYanSdk.getCurrentCarrierCount() == 0 { + DLToast.show(text: "请插入SIM卡后重试") + return + } + GeYanSdk.preGetToken { preDic in + guard let dict = preDic as? [String: Any], + dict["code"] as? Int == 30000, + let model = GyContentModel.current(), + !model.pn.isEmpty else { + DLToast.showError(text: "获取手机号失败,请使用验证码登录") + return + } + let oneTapView = OneTapLoginView(frame: UIScreen.main.bounds) + self?.viewModel.performOneTapLogin(from: self!, contentView: oneTapView) + } + #endif }) .disposed(by: disposeBag) - viewModel.loginSuccess - .subscribe(onNext: { [weak self] in - - }) - .disposed(by: disposeBag) +// viewModel.loginSuccess +// .subscribe(onNext: { [weak self] in +// +// }) +// .disposed(by: disposeBag) } // MARK: - API 验证码 @@ -150,3 +174,40 @@ extension LoginViewController: UITextViewDelegate { private func openURL(_ url: String) {} } + +// MARK: - Apple Sign In +extension LoginViewController: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding { + + func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { + view.window ?? UIWindow() + } + + func authorizationController(controller: ASAuthorizationController, + didCompleteWithAuthorization authorization: ASAuthorization) { + guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else { return } + + let userId = credential.user + let userName: String = { + if let name = credential.fullName { + let parts = [name.givenName, name.familyName].compactMap { $0 } + if !parts.isEmpty { return parts.joined(separator: " ") } + } + return "" + }() + let identityToken = credential.identityToken.flatMap { String(data: $0, encoding: .utf8) } ?? "" + let authCode = credential.authorizationCode.flatMap { String(data: $0, encoding: .utf8) } ?? "" + + viewModel.loginAction(type: "apple", data: [ + "user_id": userId, + "user_name": userName, + "identify_code": authCode, + "identify_token": identityToken + ]) + } + + func authorizationController(controller: ASAuthorizationController, + didCompleteWithError error: Error) { + NotificationCenter.default.post(name: .resumePopupQueue, object: nil) + DLToast.showError(text: "Apple 登录失败") + } +} diff --git a/QuickLocation/Section/Login/LoginViewModel.swift b/QuickLocation/Section/Login/LoginViewModel.swift index e23fe43..e808d8c 100644 --- a/QuickLocation/Section/Login/LoginViewModel.swift +++ b/QuickLocation/Section/Login/LoginViewModel.swift @@ -6,10 +6,43 @@ import Foundation import RxSwift import SwiftyUserDefaults +import AuthenticationServices #if !targetEnvironment(simulator) import GeYanSdk #endif +enum LoginSessionHandler { + private static let disposeBag = DisposeBag() + + static func login(type: String, data: [String: Any]) { + DLToast.showLoading() + UserService.login(type: type, data: data) + .delaySubscription(.seconds(1), scheduler: MainScheduler.instance) + .retry(1) + .subscribe(onNext: { response in + GroupIMService.shared.logout() + DLToast.dismiss() + guard let model = response.model else { return } + NotificationCenter.default.post(name: .invalidatePopupQueue, object: nil) + Defaults[\.loginToken] = model.token + DLToast.showSuccess(text: "登录成功") { + if let userId = model.uid { + MQTTService.shared.updateClientID("smartdrive_\(userId)") + } + NotificationCenter.default.post(name: .RefreshUserConfigNotification, object: nil) + if let nav = AppRouter.shared.navigationController, nav.viewControllers.count > 1 { + AppRouter.shared.popOrDismiss() + } else { + AppDelegate.shared.showMainViewController() + } + } + }, onError: { error in + DLToast.dismiss() + DLToast.showError(text: "登录失败,请重试") + }).disposed(by: disposeBag) + } +} + final class LoginViewModel: BaseViewModel { /// 验证码接口返回值 @@ -18,47 +51,78 @@ final class LoginViewModel: BaseViewModel { let oneClickLoginResult = PublishSubject() let loginSuccess = PublishSubject() - var gyAuthVM: GyAuthViewModel { + var onAppleLoginRequest: ((ASAuthorizationController) -> Void)? + + #if !targetEnvironment(simulator) + lazy var gyAuthVM: GyAuthViewModel = { let viewModel = GyAuthViewModel() viewModel.statusBarStyle = .default viewModel.userInterfaceStyle = 0 - viewModel.pullAuthVCStyle = .push - viewModel.viewLifeCycleBlock = { (str, bool) in - - } + viewModel.pullAuthVCStyle = .modal viewModel.clickAuthButtonBlock = { return true } return viewModel - } + }() + #endif /// 登录 func loginAction(type: String, data: [String : Any]) { - DLToast.showLoading() - UserService.login(type: type, data: data).subscribe { response in - GroupIMService.shared.logout() - DLToast.dismiss() - guard let model = response.model else { return } - Defaults[\.loginToken] = model.token - DLToast.showSuccess(text: "登录成功") { - if let userId = model.uid { - MQTTService.shared.updateClientID("smartdrive_\(userId)") - } - NotificationCenter.default.post(name: .RefreshUserConfigNotification, object: nil) - AppRouter.shared.popOrDismiss() - } - }.disposed(by: disposeBag) + LoginSessionHandler.login(type: type, data: data) } func performGuestLogin() { loginSuccess.onNext(()) } + #if !targetEnvironment(simulator) + func performOneTapLogin(from vc: UIViewController, contentView: OneTapLoginView) { + NotificationCenter.default.post(name: .pausePopupQueue, object: nil) + let subViews: [UIView] = [ + contentView.phoneNumberLabel, + contentView.carrierLabel, + contentView.loginButton, + contentView.checkBox, + contentView.agreementTV + ] + GeYanSdk.oneTapLogin(vc, viewModel: gyAuthVM, contentView: contentView, + subViewList: subViews) { [weak self] result in + guard let dict = result as? [String: Any], + dict["code"] as? Int == 30000, + let token = dict["token"] as? String else { return } + + let opType: String = { + switch dict["operatorType"] as? Int { + case 1: return "CM" + case 2: return "CU" + case 3: return "CT" + default: return "" + } + }() + + self?.loginAction(type: "onekey", data: [ + "auth_code": "", + "access_code": token, + "operator_type": opType + ]) + } + } + #endif + func performWechatLogin() { - // TODO: Integrate WeChat SDK + NotificationCenter.default.post(name: .pausePopupQueue, object: nil) + let req = SendAuthReq() + req.scope = "snsapi_userinfo" + req.state = "login" + WXApi.send(req) } func performAppleLogin() { - // TODO: Integrate Sign in with Apple + NotificationCenter.default.post(name: .pausePopupQueue, object: nil) + let provider = ASAuthorizationAppleIDProvider() + let request = provider.createRequest() + request.requestedScopes = [.fullName, .email] + let controller = ASAuthorizationController(authorizationRequests: [request]) + onAppleLoginRequest?(controller) } } diff --git a/QuickLocation/Section/Login/OneTapLoginView.swift b/QuickLocation/Section/Login/OneTapLoginView.swift index 0d8bc1f..a90740f 100644 --- a/QuickLocation/Section/Login/OneTapLoginView.swift +++ b/QuickLocation/Section/Login/OneTapLoginView.swift @@ -19,7 +19,9 @@ class OneTapLoginView: UIView { private func setupRx() { backBtn.rx.tap.subscribe(onNext: { _ in - AppRouter.shared.popOrDismiss() + #if !targetEnvironment(simulator) + GeYanSdk.closeAuthVC(true, completion: nil) + #endif }).disposed(by: disposeBag) checkBox.rx.tap.subscribe(onNext: { [weak self] in @@ -55,11 +57,12 @@ class OneTapLoginView: UIView { private func setupUI() { addSubview(bgMaskImage) addSubview(backBtn) - addSubview(welcomeLabel) - addSubview(phoneNumberLabel) - addSubview(carrierLabel) - addSubview(loginButton) - + addSubview(inputContainerView) + inputContainerView.addSubview(welcomeLabel) + inputContainerView.addSubview(phoneNumberLabel) + inputContainerView.addSubview(carrierLabel) + inputContainerView.addSubview(loginButton) + addSubview(agreementView) agreementView.addSubview(checkBox) agreementView.addSubview(agreementTV) @@ -73,8 +76,14 @@ class OneTapLoginView: UIView { .width(24) .height(24) + inputContainerView.layoutChain + .centerX() + .centerY() + .edgesHorzontal() + .height(301) + welcomeLabel.layoutChain - .top(202 - kNaviHeight) + .top(0) .centerX() .edgesHorzontal() @@ -130,6 +139,12 @@ class OneTapLoginView: UIView { } // MARK: - UI Components + lazy var inputContainerView: UIView = { + let view = UIView() + view.backgroundColor = .clear + return view + }() + lazy var bgMaskImage: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "Login/bg") @@ -207,7 +222,6 @@ class OneTapLoginView: UIView { textView.backgroundColor = .clear textView.isEditable = false textView.isScrollEnabled = false - textView.isSelectable = false textView.linkTextAttributes = [:] return textView diff --git a/QuickLocation/Section/Mine/Account/AccountVC.swift b/QuickLocation/Section/Mine/Account/AccountVC.swift index 77d2e6f..154faf4 100644 --- a/QuickLocation/Section/Mine/Account/AccountVC.swift +++ b/QuickLocation/Section/Mine/Account/AccountVC.swift @@ -59,6 +59,7 @@ class AccountVC: BaseViewController { DLToast.showLoading() UserService.logout().subscribe(onNext: { response in DLToast.dismiss() + NotificationCenter.default.post(name: .invalidatePopupQueue, object: nil) AppContextManager.shared.deleteAccount() GroupIMService.shared.logout() AppDelegate.shared.showMainViewController() diff --git a/QuickLocation/Section/Mine/MineView.swift b/QuickLocation/Section/Mine/MineView.swift index 7637134..69f3b19 100644 --- a/QuickLocation/Section/Mine/MineView.swift +++ b/QuickLocation/Section/Mine/MineView.swift @@ -188,6 +188,10 @@ final class MineView: UIView { // MARK: - Callbacks var onMenuTap: ((Int) -> Void)? + // MARK: - Dynamic Height + private var settingsCardHeightConstraint: NSLayoutConstraint? + private var contentSizeObservation: NSKeyValueObservation? + // MARK: - Init override init(frame: CGRect) { super.init(frame: frame) @@ -289,14 +293,42 @@ final class MineView: UIView { .topToBottomOfView(profileBgImageView) .left().right().bottom() - // Settings Card (fixed height, not stretched) + // Settings Card (dynamic height, scrolls when exceeds area above tabbar) settingsCardView.layoutChain .topToBottomOfView(vipCardView, offset: 20) .left(16).right(16) - .height(312) + + settingsCardHeightConstraint = settingsCardView.heightAnchor.constraint(equalToConstant: 312) + settingsCardHeightConstraint?.isActive = true settingsTableView.layoutChain .edges() + + contentSizeObservation = settingsTableView.observe(\.contentSize, options: [.new, .initial]) { [weak self] _, _ in + self?.refreshTableViewHeight() + } + } + + // MARK: - Layout + override func layoutSubviews() { + super.layoutSubviews() + refreshTableViewHeight() + } + + func refreshTableViewHeight() { + let contentHeight = settingsTableView.contentSize.height + guard contentHeight > 0 else { return } + + let tabBarHeight: CGFloat = 56 + kSafeBottomMargin + let maxHeight = bounds.height - tabBarHeight - settingsCardView.frame.minY + + if contentHeight <= maxHeight { + settingsCardHeightConstraint?.constant = contentHeight + settingsTableView.isScrollEnabled = false + } else { + settingsCardHeightConstraint?.constant = maxHeight + settingsTableView.isScrollEnabled = true + } } } diff --git a/QuickLocation/Section/PopupWindow/PopupQueueManager.swift b/QuickLocation/Section/PopupWindow/PopupQueueManager.swift index 4f23b2c..86dfffb 100644 --- a/QuickLocation/Section/PopupWindow/PopupQueueManager.swift +++ b/QuickLocation/Section/PopupWindow/PopupQueueManager.swift @@ -11,7 +11,28 @@ class PopupQueueManager { private weak var sourceVC: UIViewController? private var currentStep = 0 + private var stepGeneration = 0 private var countdownTimer: Timer? + private var isPaused = false + + init() { + NotificationCenter.default.addObserver(self, + selector: #selector(handlePause), + name: .pausePopupQueue, + object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(handleResume), + name: .resumePopupQueue, + object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(handleInvalidate), + name: .invalidatePopupQueue, + object: nil) + } + + deinit { + NotificationCenter.default.removeObserver(self) + } func start(from vc: UIViewController) { sourceVC = vc @@ -19,6 +40,25 @@ class PopupQueueManager { executeNextStep() } + @objc private func handlePause() { + countdownTimer?.invalidate() + countdownTimer = nil + isPaused = true + } + + @objc private func handleResume() { + guard isPaused else { return } + isPaused = false + executeNextStep() + } + + @objc private func handleInvalidate() { + countdownTimer?.invalidate() + countdownTimer = nil + isPaused = true + currentStep = 3 + } + private func executeNextStep() { switch currentStep { case 0: @@ -45,12 +85,14 @@ class PopupQueueManager { skipToNext() return } + let gen = stepGeneration startCountdown(seconds: timeConfig.pay_pop_time) { [weak self] in + guard let self = self, self.currentStep == 0, self.stepGeneration == gen else { return } let vc = PromotionalActivitiesVC() vc.onDismiss = { [weak self] in self?.skipToNext() } - self?.push(vc) + self.push(vc) } } @@ -66,9 +108,11 @@ class PopupQueueManager { skipToNext() return } - startCountdown(seconds: timeConfig.search_phone) { [weak self] in + let gen = stepGeneration + startCountdown(seconds: timeConfig.search_phone) { + guard self.currentStep == 1, self.stepGeneration == gen else { return } PopupWindow.show(popupType: .search) { - self?.skipToNext() + self.skipToNext() } } } @@ -82,13 +126,17 @@ class PopupQueueManager { } if AppContextManager.shared.isGuest { - startCountdown(seconds: timeConfig.signup_tip) { [weak self] in + let gen = stepGeneration + startCountdown(seconds: timeConfig.signup_tip) { + guard self.currentStep == 2, self.stepGeneration == gen else { return } PopupWindow.show(popupType: .register) { // done } } } else if AppContextManager.shared.vip == 1 { - startCountdown(seconds: timeConfig.buy_vip_tip) { [weak self] in + let gen = stepGeneration + startCountdown(seconds: timeConfig.buy_vip_tip) { + guard self.currentStep == 2, self.stepGeneration == gen else { return } PopupWindow.show(popupType: .vip) { // done } @@ -99,6 +147,7 @@ class PopupQueueManager { // MARK: - Helpers private func skipToNext() { + stepGeneration += 1 currentStep += 1 executeNextStep() } @@ -122,7 +171,11 @@ class PopupQueueManager { private func push(_ vc: UIViewController) { let topVC = topViewController() - topVC?.navigationController?.pushViewController(vc, animated: true) + guard let nav = topVC?.navigationController else { return } + if nav.viewControllers.contains(where: { type(of: $0) == type(of: vc) }) { + return + } + nav.pushViewController(vc, animated: true) } private func topViewController() -> UIViewController? { diff --git a/QuickLocation/Section/PopupWindow/PopupWindow.swift b/QuickLocation/Section/PopupWindow/PopupWindow.swift index 7495636..dc2722d 100644 --- a/QuickLocation/Section/PopupWindow/PopupWindow.swift +++ b/QuickLocation/Section/PopupWindow/PopupWindow.swift @@ -151,8 +151,12 @@ class PopupWindow: UIView { registerBtn.rx.tap .subscribe(onNext: { [weak self] _ in + NotificationCenter.default.post(name: .pausePopupQueue, object: nil) self?.activePopupView == 2 ? self?.slideOutRegister2Popup() : self?.slideOutRegisterPopup() - AppRouter.push(Route.login) + let topVC = UIViewController.currentViewController() + if !(topVC is LoginViewController) { + AppRouter.push(Route.login) + } }) .disposed(by: disposeBag) @@ -164,8 +168,12 @@ class PopupWindow: UIView { register2Btn.rx.tap .subscribe(onNext: { [weak self] _ in + NotificationCenter.default.post(name: .pausePopupQueue, object: nil) self?.slideOutRegister2Popup() - AppRouter.push(Route.login) + let topVC = UIViewController.currentViewController() + if !(topVC is LoginViewController) { + AppRouter.push(Route.login) + } }) .disposed(by: disposeBag) @@ -178,6 +186,10 @@ class PopupWindow: UIView { vip1UnlockBtn.rx.tap .subscribe(onNext: { [weak self] _ in self?.slideOutUnlockVip1Popup() + let topVC = UIViewController.currentViewController() + if !(topVC is VipRechargeVC) { + AppRouter.push(Route.vipRecharge) + } }) .disposed(by: disposeBag) @@ -190,6 +202,10 @@ class PopupWindow: UIView { unlockVip2Btn.rx.tap .subscribe(onNext: { [weak self] _ in self?.slideOutUnlockVip2Popup() + let topVC = UIViewController.currentViewController() + if !(topVC is VipRechargeVC) { + AppRouter.push(Route.vipRecharge) + } }) .disposed(by: disposeBag) @@ -202,6 +218,10 @@ class PopupWindow: UIView { unlockVip3Btn.rx.tap .subscribe(onNext: { [weak self] _ in self?.slideOutUnlockVip3Popup() + let topVC = UIViewController.currentViewController() + if !(topVC is VipRechargeVC) { + AppRouter.push(Route.vipRecharge) + } }) .disposed(by: disposeBag) @@ -214,6 +234,10 @@ class PopupWindow: UIView { unlockVip4Btn.rx.tap .subscribe(onNext: { [weak self] _ in self?.slideOutUnlockVip4Popup() + let topVC = UIViewController.currentViewController() + if !(topVC is VipRechargeVC) { + AppRouter.push(Route.vipRecharge) + } }) .disposed(by: disposeBag) } diff --git a/QuickLocation/Section/PopupWindow/PromotionalActivitiesVC.swift b/QuickLocation/Section/PopupWindow/PromotionalActivitiesVC.swift index ed672b2..e0c734e 100644 --- a/QuickLocation/Section/PopupWindow/PromotionalActivitiesVC.swift +++ b/QuickLocation/Section/PopupWindow/PromotionalActivitiesVC.swift @@ -49,7 +49,7 @@ class PromotionalActivitiesVC: BaseViewController { override func viewDidLoad() { super.viewDidLoad() - + fd_interactivePopDisabled = true setupTableView() requestRechargeInfo() @@ -103,6 +103,11 @@ class PromotionalActivitiesVC: BaseViewController { } } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + navigationController?.setNavigationBarHidden(true, animated: false) + } + // MARK: - API private func requestRechargeInfo() { DLToast.showLoading() @@ -126,29 +131,29 @@ class PromotionalActivitiesVC: BaseViewController { return } - if timeConfig.pay_pop_type == 3 { - IAPManager.shared.purchase(goodsId: selectedGoodsId) { [weak self] success, msg in - guard let self = self else { return } - if success { - self.showPayResultPop(showCloseBtn: false, - status: true, - title: "支付成功", - message: "恭喜您!成功开通!", - confirmText: "确定", confirmBlock: { - AppRouter.shared.popOrDismiss() - }, cancelBlock: { }) - } else { - self.showPayResultPop(status: false, - title: "支付失败", - message: msg ?? "很抱歉,请您重新支付!", - confirmText: "重新支付", confirmBlock: { }, - cancelText: "取消", cancelBlock: { - AppRouter.shared.popOrDismiss() - }) - } - } - return - } +// if timeConfig.pay_pop_type == 3 { +// IAPManager.shared.purchase(goodsId: selectedGoodsId) { [weak self] success, msg in +// guard let self = self else { return } +// if success { +// self.showPayResultPop(showCloseBtn: false, +// status: true, +// title: "支付成功", +// message: "恭喜您!成功开通!", +// confirmText: "确定", confirmBlock: { +// AppRouter.shared.popOrDismiss() +// }, cancelBlock: { }) +// } else { +// self.showPayResultPop(status: false, +// title: "支付失败", +// message: msg ?? "很抱歉,请您重新支付!", +// confirmText: "重新支付", confirmBlock: { }, +// cancelText: "取消", cancelBlock: { +// AppRouter.shared.popOrDismiss() +// }) +// } +// } +// return +// } let payType = timeConfig.pay_pop_type == 1 ? "alipay" : "weixin" dl.showLoading() diff --git a/QuickLocation/Section/PopupWindow/PromotionalActivitiesView.swift b/QuickLocation/Section/PopupWindow/PromotionalActivitiesView.swift index 0be5959..aef7aa7 100644 --- a/QuickLocation/Section/PopupWindow/PromotionalActivitiesView.swift +++ b/QuickLocation/Section/PopupWindow/PromotionalActivitiesView.swift @@ -533,7 +533,7 @@ class PromotionalRecommendCell: UITableViewCell { tipsTitleLab.layoutChain .centerX() - .top(5) + .top(7) priceLab.layoutChain .leftToRightOfView(unitLab, offset: 0) diff --git a/QuickLocation/Section/Schedule/ScheduleVC.swift b/QuickLocation/Section/Schedule/ScheduleVC.swift index 867f6fe..b74cb08 100644 --- a/QuickLocation/Section/Schedule/ScheduleVC.swift +++ b/QuickLocation/Section/Schedule/ScheduleVC.swift @@ -55,6 +55,16 @@ class ScheduleVC: BaseViewController { self.rootView.tableView.refresh(status: status, isEmpty: isEmpty) }).disposed(by: disposeBag) + viewModel.output.sectionedItems + .map { !($0.first?.items.isEmpty ?? true) } + .bind(to: rootView.emptyVisitorsLabel.rx.isHidden) + .disposed(by: disposeBag) + + viewModel.output.scheduleSectionedItems + .map { !($0.first?.items.isEmpty ?? true) } + .bind(to: rootView.emptyScheduleLabel.rx.isHidden) + .disposed(by: disposeBag) + rootView.collectionView.rx.modelSelected(ViewedModel.self) .subscribe(viewModel.viewedCellAction.inputs) .disposed(by: disposeBag) diff --git a/QuickLocation/Section/Schedule/ScheduleView.swift b/QuickLocation/Section/Schedule/ScheduleView.swift index 27ad4c7..4134c20 100644 --- a/QuickLocation/Section/Schedule/ScheduleView.swift +++ b/QuickLocation/Section/Schedule/ScheduleView.swift @@ -128,6 +128,12 @@ class ScheduleView: UIView { .edgesHorzontal() .height(80) .bottom(15) + + // 暂无访客 + view.addSubview(emptyVisitorsLabel) + emptyVisitorsLabel.layoutChain + .centerY(collectionView) + .centerX() return view }() @@ -145,6 +151,16 @@ class ScheduleView: UIView { return cv }() + lazy var emptyVisitorsLabel: UILabel = { + let label = UILabel() + label.text = "暂无访客" + label.font = .systemFont(ofSize: 14) + label.textColor = UIColor(hexStr: "#999999") + label.textAlignment = .center + label.isHidden = true + return label + }() + /// 行程路线 lazy var travelRouteView: UIView = { let view = UIView() @@ -213,9 +229,20 @@ class ScheduleView: UIView { tableView.showsVerticalScrollIndicator = false tableView.register(ScheduleListPopCell.self) tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 97 + kSafeBottomMargin, right: 0) + tableView.backgroundView = emptyScheduleLabel return tableView }() + lazy var emptyScheduleLabel: UILabel = { + let label = UILabel() + label.text = "暂无行程" + label.font = .systemFont(ofSize: 14) + label.textColor = UIColor(hexStr: "#999999") + label.textAlignment = .center + label.isHidden = true + return label + }() + lazy var createBtn: UIButton = { let btn = UIButton() btn.setImage(UIImage(named: "Schedule/create"), for: .normal) diff --git a/QuickLocation/Section/Share/SharePopView.swift b/QuickLocation/Section/Share/SharePopView.swift index bd770b8..a6916f7 100644 --- a/QuickLocation/Section/Share/SharePopView.swift +++ b/QuickLocation/Section/Share/SharePopView.swift @@ -6,22 +6,22 @@ // import UIKit +import Kingfisher class SharePopView: UIView { private static let shared = SharePopView(frame: CGRect(origin: .zero, size: kScreenSize)) private var shareURL: String = "" - + private let contentHeight: CGFloat = 191 + kSafeBottomMargin - static func show(shareURL: String) { + static func show() { guard let superView = kKeyWindow else { return } if SharePopView.shared.superview != nil { SharePopView.shared.dismiss(animated: false) } - SharePopView.shared.shareURL = shareURL SharePopView.shared.bgView.alpha = 0 superView.addSubview(SharePopView.shared) superView.bringSubviewToFront(SharePopView.shared) @@ -65,19 +65,52 @@ class SharePopView: UIView { } private func shareToWeChat(scene: Int32) { - let message = WXMediaMessage() - message.title = kAppName - let webpage = WXWebpageObject() - webpage.webpageUrl = shareURL - message.mediaObject = webpage + guard let config = AppContextManager.shared.systemConfig, + let shareConfig = config.shareConfig else { return } - let req = SendMessageToWXReq() - req.bText = false - req.message = message - req.scene = scene - WXApi.send(req) + DLToast.showLoading() - dismiss() + let fallbackImage = UIImage(named: "Common/logo108") + let imageURL: URL? = { + if shareConfig.image.isEmpty { return nil } + return URL(string: shareConfig.image) + }() + + let sendMessage: (UIImage?) -> Void = { [weak self] thumbImage in + guard let self = self else { return } + DLToast.dismiss() + let message = WXMediaMessage() + message.title = shareConfig.title + message.description = shareConfig.content + if let thumb = thumbImage { + message.setThumbImage(thumb) + } + + let webpage = WXWebpageObject() + webpage.webpageUrl = shareConfig.link + message.mediaObject = webpage + + let req = SendMessageToWXReq() + req.bText = false + req.message = message + req.scene = scene + WXApi.send(req) + + self.dismiss() + } + + if let url = imageURL { + KingfisherManager.shared.retrieveImage(with: url) { result in + switch result { + case .success(let value): + sendMessage(value.image) + case .failure: + sendMessage(fallbackImage) + } + } + } else { + sendMessage(fallbackImage) + } } // MARK: - UI diff --git a/QuickLocation/Section/VipRecharge/VipRechargeVC.swift b/QuickLocation/Section/VipRecharge/VipRechargeVC.swift index 9081cb0..f83c9dc 100644 --- a/QuickLocation/Section/VipRecharge/VipRechargeVC.swift +++ b/QuickLocation/Section/VipRecharge/VipRechargeVC.swift @@ -29,6 +29,11 @@ class VipRechargeVC: BaseViewController { requestRechargeInfo() } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + navigationController?.setNavigationBarHidden(true, animated: false) + } + private func bindViewModel() { viewModel.output.sectionedItems .bind(to: rootView.expenseCollectionView.rx.items(dataSource: dataSource)) @@ -102,29 +107,29 @@ class VipRechargeVC: BaseViewController { let types = viewModel.payType.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) } let payType = types[rootView.selectedPayTypeTag] - if payType == "apple" { - IAPManager.shared.purchase(goodsId: viewModel.goodsId) { [weak self] success, msg in - guard let self = self else { return } - if success { - self.showPayResultPop(showCloseBtn: false, - status: true, - title: "支付成功", - message: "恭喜您!成功开通\(self.viewModel.goodsName)", - confirmText: "确定", confirmBlock: { - AppRouter.shared.popOrDismiss() - }, cancelBlock: { }) - } else { - self.showPayResultPop(status: false, - title: "支付失败", - message: msg ?? "很抱歉,请您重新支付!", - confirmText: "重新支付", confirmBlock: { }, - cancelText: "取消", cancelBlock: { - AppRouter.shared.popOrDismiss() - }) - } - } - return - } +// if payType == "apple" { +// IAPManager.shared.purchase(goodsId: viewModel.goodsId) { [weak self] success, msg in +// guard let self = self else { return } +// if success { +// self.showPayResultPop(showCloseBtn: false, +// status: true, +// title: "支付成功", +// message: "恭喜您!成功开通\(self.viewModel.goodsName)", +// confirmText: "确定", confirmBlock: { +// AppRouter.shared.popOrDismiss() +// }, cancelBlock: { }) +// } else { +// self.showPayResultPop(status: false, +// title: "支付失败", +// message: msg ?? "很抱歉,请您重新支付!", +// confirmText: "重新支付", confirmBlock: { }, +// cancelText: "取消", cancelBlock: { +// AppRouter.shared.popOrDismiss() +// }) +// } +// } +// return +// } DLToast.showLoading() OrderService.orderPayParams(goodsId: viewModel.goodsId, diff --git a/QuickLocation/Section/VipRecharge/VipRechargeView.swift b/QuickLocation/Section/VipRecharge/VipRechargeView.swift index ebc3ae5..e2209a3 100644 --- a/QuickLocation/Section/VipRecharge/VipRechargeView.swift +++ b/QuickLocation/Section/VipRecharge/VipRechargeView.swift @@ -99,7 +99,7 @@ class VipRechargeView: UIView { .heightToWidth(267/375) vipRightsDetailView.layoutChain - .top(36) + .top(36 + kStatusBarHeight) .right() .height(24) @@ -562,7 +562,7 @@ class VipRechargeView: UIView { let payTypeMap: [(String, String)] = types.compactMap { if $0 == "weixin" { return ("wechat", "微信支付") } if $0 == "alipay" { return ("alipay", "支付宝支付") } - if $0 == "apple" { return ("apple", "苹果支付") } +// if $0 == "apple" { return ("apple", "苹果支付") } return nil } guard !payTypeMap.isEmpty else { return }