// // EPLoginViewController.swift // YuMi // // Created by AI on 2025-01-27. // import UIKit @objc class EPLoginViewController: UIViewController { // MARK: - Properties private let backgroundImageView = UIImageView() private let logoImageView = UIImageView() private let epartiTitleLabel = UILabel() private let idLoginButton = EPLoginButton() private let emailLoginButton = EPLoginButton() private let agreeCheckbox = UIButton(type: .custom) private let policyLabel = EPPolicyLabel() private let feedbackButton = UIButton(type: .custom) #if DEBUG private let debugButton = UIButton(type: .custom) #endif private let policySelectedKey = EPLoginConfig.Keys.policyAgreed // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() navigationController?.setNavigationBarHidden(true, animated: false) setupUI() loadPolicyStatus() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) } // MARK: - Setup private func setupUI() { setupBackground() setupLogo() setupLoginButtons() setupPolicyArea() setupNavigationBar() } private func setupBackground() { view.addSubview(backgroundImageView) backgroundImageView.image = kImage(EPLoginConfig.Images.background) backgroundImageView.contentMode = .scaleAspectFill backgroundImageView.snp.makeConstraints { make in make.edges.equalToSuperview() } } private func setupLogo() { view.addSubview(logoImageView) logoImageView.image = kImage(EPLoginConfig.Images.loginBg) logoImageView.snp.makeConstraints { make in make.top.leading.trailing.equalTo(view) make.height.equalTo(EPLoginConfig.Layout.logoHeight) } // E-PARTI 标题 view.addSubview(epartiTitleLabel) epartiTitleLabel.text = "E-PARTI" epartiTitleLabel.font = .systemFont(ofSize: EPLoginConfig.Layout.epartiTitleFontSize, weight: .bold) epartiTitleLabel.textColor = EPLoginConfig.Colors.textLight epartiTitleLabel.transform = CGAffineTransform(a: 1, b: 0, c: -0.2, d: 1, tx: 0, ty: 0) // 斜体效果 epartiTitleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().offset(EPLoginConfig.Layout.epartiTitleLeading) make.bottom.equalTo(logoImageView.snp.bottom).offset(EPLoginConfig.Layout.epartiTitleBottomOffset) } } private func setupLoginButtons() { // 配置按钮 idLoginButton.configure( icon: EPLoginConfig.Images.iconLoginId, title: YMLocalizedString(EPLoginConfig.LocalizedKeys.idLogin) ) idLoginButton.delegate = self emailLoginButton.configure( icon: EPLoginConfig.Images.iconLoginEmail, title: YMLocalizedString(EPLoginConfig.LocalizedKeys.emailLogin) ) emailLoginButton.delegate = self // StackView 布局 let stackView = UIStackView(arrangedSubviews: [idLoginButton, emailLoginButton]) stackView.axis = .vertical stackView.spacing = EPLoginConfig.Layout.loginButtonSpacing stackView.distribution = .fillEqually view.addSubview(stackView) stackView.snp.makeConstraints { make in make.leading.equalToSuperview().offset(EPLoginConfig.Layout.loginButtonHorizontalPadding) make.trailing.equalToSuperview().offset(-EPLoginConfig.Layout.loginButtonHorizontalPadding) make.top.equalTo(logoImageView.snp.bottom) } idLoginButton.snp.makeConstraints { make in make.height.equalTo(EPLoginConfig.Layout.loginButtonHeight) } emailLoginButton.snp.makeConstraints { make in make.height.equalTo(EPLoginConfig.Layout.loginButtonHeight) } } private func setupPolicyArea() { view.addSubview(agreeCheckbox) view.addSubview(policyLabel) agreeCheckbox.setImage(kImage("login_privace_select"), for: .selected) agreeCheckbox.setImage(kImage("login_privace_unselect"), for: .normal) agreeCheckbox.addTarget(self, action: #selector(togglePolicyCheckbox), for: .touchUpInside) policyLabel.onUserAgreementTapped = { [weak self] in print("[EPLogin] User agreement tapped callback triggered") let url = self?.getUserAgreementURL() ?? "" print("[EPLogin] User agreement URL: \(url)") self?.openPolicyInExternalBrowser(url) } policyLabel.onPrivacyPolicyTapped = { [weak self] in print("[EPLogin] Privacy policy tapped callback triggered") let url = self?.getPrivacyPolicyURL() ?? "" print("[EPLogin] Privacy policy URL: \(url)") self?.openPolicyInExternalBrowser(url) } agreeCheckbox.snp.makeConstraints { make in make.leading.equalToSuperview().offset(EPLoginConfig.Layout.horizontalPadding) make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-30) make.size.equalTo(EPLoginConfig.Layout.checkboxSize) } policyLabel.snp.makeConstraints { make in make.leading.equalTo(agreeCheckbox.snp.trailing).offset(8) make.trailing.equalToSuperview().offset(-EPLoginConfig.Layout.horizontalPadding) make.centerY.equalTo(agreeCheckbox) } } private func setupNavigationBar() { view.addSubview(feedbackButton) feedbackButton.setTitle(YMLocalizedString(EPLoginConfig.LocalizedKeys.feedback), for: .normal) feedbackButton.titleLabel?.font = .systemFont(ofSize: EPLoginConfig.Layout.smallFontSize) feedbackButton.backgroundColor = EPLoginConfig.Colors.backgroundTransparent feedbackButton.layer.cornerRadius = EPLoginConfig.Layout.feedbackButtonCornerRadius feedbackButton.addTarget(self, action: #selector(handleFeedback), for: .touchUpInside) feedbackButton.snp.makeConstraints { make in make.trailing.equalToSuperview().offset(-EPLoginConfig.Layout.compactHorizontalPadding) make.top.equalTo(view.safeAreaLayoutGuide).offset(8) make.height.equalTo(EPLoginConfig.Layout.feedbackButtonHeight) } #if DEBUG view.addSubview(debugButton) debugButton.setTitle("切换环境", for: .normal) debugButton.setTitleColor(.blue, for: .normal) debugButton.addTarget(self, action: #selector(handleDebug), for: .touchUpInside) debugButton.snp.makeConstraints { make in make.leading.equalToSuperview().offset(EPLoginConfig.Layout.compactHorizontalPadding) make.top.equalTo(view.safeAreaLayoutGuide).offset(8) } #endif } // MARK: - Actions private func handleIDLogin() { let vc = EPLoginTypesViewController() vc.displayType = .id navigationController?.pushViewController(vc, animated: true) } private func handleEmailLogin() { let vc = EPLoginTypesViewController() vc.displayType = .email navigationController?.pushViewController(vc, animated: true) } @objc private func togglePolicyCheckbox() { agreeCheckbox.isSelected.toggle() UserDefaults.standard.set(agreeCheckbox.isSelected, forKey: policySelectedKey) } @objc private func handleFeedback() { print("[EPLogin] Feedback - 占位,Phase 2 实现") } #if DEBUG @objc private func handleDebug() { print("[EPLogin] Debug - 占位,Phase 2 实现") } #endif private func openPolicyInExternalBrowser(_ urlString: String) { print("[EPLogin] Original URL: \(urlString)") // 如果不是完整 URL,拼接域名(参考 XPWebViewController.m 第 697-698 行) var fullUrl = urlString if !urlString.hasPrefix("http") && !urlString.hasPrefix("https") { let hostUrl = HttpRequestHelper.getHostUrl() fullUrl = "\(hostUrl)/\(urlString)" print("[EPLogin] Added host URL, full URL: \(fullUrl)") } print("[EPLogin] Opening URL in external browser: \(fullUrl)") guard let url = URL(string: fullUrl) else { print("[EPLogin] ❌ Invalid URL: \(fullUrl)") return } print("[EPLogin] URL object created: \(url)") // 在外部浏览器中打开 if UIApplication.shared.canOpenURL(url) { print("[EPLogin] ✅ Can open URL, attempting to open...") UIApplication.shared.open(url, options: [:]) { success in print("[EPLogin] Open external browser: \(success ? "✅ Success" : "❌ Failed")") } } else { print("[EPLogin] ❌ Cannot open URL: \(fullUrl)") } } // MARK: - Helpers private func loadPolicyStatus() { agreeCheckbox.isSelected = UserDefaults.standard.bool(forKey: policySelectedKey) // 默认勾选 if !UserDefaults.standard.bool(forKey: EPLoginConfig.Keys.hasLaunchedBefore) { agreeCheckbox.isSelected = true UserDefaults.standard.set(true, forKey: policySelectedKey) UserDefaults.standard.set(true, forKey: EPLoginConfig.Keys.hasLaunchedBefore) } } /// 获取用户协议 URL private func getUserAgreementURL() -> String { // kUserProtocalURL 对应枚举值 4 let url = URLWithType(URLType(rawValue: 4)!) as String print("[EPLogin] User agreement URL from URLWithType: \(url)") return url } /// 获取隐私政策 URL private func getPrivacyPolicyURL() -> String { // kPrivacyURL 对应枚举值 0 let url = URLWithType(URLType(rawValue: 0)!) as String print("[EPLogin] Privacy policy URL from URLWithType: \(url)") return url } private func checkPolicyAgreed() -> Bool { if !agreeCheckbox.isSelected { // Phase 2: 显示提示 print("[EPLogin] Please agree to policy first") return false } return true } } // MARK: - EPLoginButtonDelegate extension EPLoginViewController: EPLoginButtonDelegate { func loginButtonDidTap(_ button: EPLoginButton) { guard checkPolicyAgreed() else { return } if button == idLoginButton { handleIDLogin() } else if button == emailLoginButton { handleEmailLogin() } } }