// // 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 self?.openPolicy(url: "https://example.com/user-agreement") } policyLabel.onPrivacyPolicyTapped = { [weak self] in self?.openPolicy(url: "https://example.com/privacy-policy") } 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 openPolicy(url: String) { let webVC = XPWebViewController(roomUID: nil) webVC.url = url navigationController?.pushViewController(webVC, animated: true) } // 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) } } 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() } } }