From 02a8335d7045334ca1110de20ef1663eb9d2081a Mon Sep 17 00:00:00 2001 From: edwinQQQ Date: Mon, 13 Oct 2025 17:49:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E4=BB=A5=E6=94=AF=E6=8C=81=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=E5=92=8C=E6=B8=90=E5=8F=98=E8=83=8C=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要变更: 1. 在 EPLoginTypesViewController 中添加了渐变背景到 actionButton,提升视觉效果。 2. 实现了输入框状态检查功能,确保在输入有效信息时启用登录按钮。 3. 更新了输入框配置,支持不同类型的键盘输入(如数字键盘和邮箱键盘)。 4. 在 EPLoginService 中添加了对手机号和邮箱的 DES 加密,增强安全性。 5. 更新了 EPLoginConfig,统一输入框和按钮的样式设置。 此更新旨在提升用户体验,确保登录过程的安全性和流畅性。 --- .../EPLoginTypesViewController.swift | 215 ++++++++++++++---- .../Controllers/EPLoginViewController.swift | 4 +- YuMi/E-P/NewLogin/Models/EPLoginBridge.swift | 13 ++ YuMi/E-P/NewLogin/Models/EPLoginConfig.swift | 31 ++- .../NewLogin/Services/EPLoginService.swift | 49 +++- .../E-P/NewLogin/Views/EPLoginInputView.swift | 41 ++-- YuMi/Modules/YMLogin/Api/Api+Login.m | 4 +- YuMi/YuMi-Bridging-Header.h | 8 + 8 files changed, 287 insertions(+), 78 deletions(-) diff --git a/YuMi/E-P/NewLogin/Controllers/EPLoginTypesViewController.swift b/YuMi/E-P/NewLogin/Controllers/EPLoginTypesViewController.swift index cae596f..7ef1a7f 100644 --- a/YuMi/E-P/NewLogin/Controllers/EPLoginTypesViewController.swift +++ b/YuMi/E-P/NewLogin/Controllers/EPLoginTypesViewController.swift @@ -27,6 +27,8 @@ class EPLoginTypesViewController: UIViewController { private let actionButton = UIButton(type: .system) private var forgotPasswordButton: UIButton? + private var hasAddedGradient = false + // MARK: - Lifecycle override func viewDidLoad() { @@ -40,6 +42,24 @@ class EPLoginTypesViewController: UIViewController { navigationController?.setNavigationBarHidden(true, animated: animated) } + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + // 添加渐变背景到 actionButton(只添加一次) + if !hasAddedGradient && actionButton.bounds.width > 0 { + actionButton.addGradientBackground( + with: [ + EPLoginConfig.Colors.gradientStart, + EPLoginConfig.Colors.gradientEnd + ], + start: CGPoint(x: 0, y: 0.5), + end: CGPoint(x: 1, y: 0.5), + cornerRadius: EPLoginConfig.Layout.uniformCornerRadius + ) + hasAddedGradient = true + } + } + // MARK: - Setup private func setupUI() { @@ -89,17 +109,16 @@ class EPLoginTypesViewController: UIViewController { view.addSubview(secondInputView) firstInputView.snp.makeConstraints { make in - make.centerX.equalToSuperview() + make.leading.equalToSuperview().offset(EPLoginConfig.Layout.uniformHorizontalPadding) + make.trailing.equalToSuperview().offset(-EPLoginConfig.Layout.uniformHorizontalPadding) make.top.equalTo(titleLabel.snp.bottom).offset(EPLoginConfig.Layout.inputTitleSpacing) - make.width.equalTo(EPLoginConfig.Layout.buttonWidth) - make.height.equalTo(EPLoginConfig.Layout.buttonHeight) + make.height.equalTo(EPLoginConfig.Layout.uniformHeight) } secondInputView.snp.makeConstraints { make in - make.centerX.equalToSuperview() + make.leading.trailing.equalTo(firstInputView) make.top.equalTo(firstInputView.snp.bottom).offset(EPLoginConfig.Layout.inputVerticalSpacing) - make.width.equalTo(EPLoginConfig.Layout.buttonWidth) - make.height.equalTo(EPLoginConfig.Layout.buttonHeight) + make.height.equalTo(EPLoginConfig.Layout.uniformHeight) } } @@ -107,16 +126,18 @@ class EPLoginTypesViewController: UIViewController { view.addSubview(actionButton) actionButton.setTitle("Login", for: .normal) actionButton.setTitleColor(EPLoginConfig.Colors.textLight, for: .normal) - actionButton.backgroundColor = EPLoginConfig.Colors.primary - actionButton.layer.cornerRadius = EPLoginConfig.Layout.cornerRadius + actionButton.layer.cornerRadius = EPLoginConfig.Layout.uniformCornerRadius actionButton.titleLabel?.font = .systemFont(ofSize: EPLoginConfig.Layout.buttonFontSize, weight: .semibold) actionButton.addTarget(self, action: #selector(handleAction), for: .touchUpInside) + // 初始状态:禁用按钮 + actionButton.isEnabled = false + actionButton.alpha = 0.5 + actionButton.snp.makeConstraints { make in - make.centerX.equalToSuperview() + make.leading.trailing.equalTo(firstInputView) make.top.equalTo(secondInputView.snp.bottom).offset(EPLoginConfig.Layout.buttonTopSpacing) - make.width.equalTo(EPLoginConfig.Layout.buttonWidth) - make.height.equalTo(EPLoginConfig.Layout.buttonHeight) + make.height.equalTo(EPLoginConfig.Layout.uniformHeight) } } @@ -130,16 +151,26 @@ class EPLoginTypesViewController: UIViewController { showAreaCode: false, showCodeButton: false, isSecure: false, - icon: "person", - placeholder: "Please enter ID" + icon: "icon_login_id", + placeholder: "Please enter ID", + keyboardType: .numberPad // ID 使用数字键盘 )) + firstInputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } + secondInputView.configure(with: EPLoginInputConfig( showAreaCode: false, showCodeButton: false, isSecure: true, - icon: "lock", - placeholder: "Please enter password" + icon: "icon_login_id", + placeholder: "Please enter password", + keyboardType: .default // 密码使用默认键盘(需要字母+数字) )) + secondInputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } + actionButton.setTitle("Login", for: .normal) // 添加忘记密码按钮 @@ -152,15 +183,24 @@ class EPLoginTypesViewController: UIViewController { showCodeButton: false, isSecure: false, icon: "envelope", - placeholder: "Please enter email" + placeholder: "Please enter email", + keyboardType: .emailAddress // Email 使用邮箱键盘 )) + firstInputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } + secondInputView.configure(with: EPLoginInputConfig( showAreaCode: false, showCodeButton: true, isSecure: false, icon: "number", - placeholder: "Please enter verification code" + placeholder: "Please enter verification code", + keyboardType: .numberPad // 验证码使用数字键盘 )) + secondInputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } secondInputView.delegate = self actionButton.setTitle("Login", for: .normal) @@ -171,15 +211,24 @@ class EPLoginTypesViewController: UIViewController { showCodeButton: false, isSecure: false, icon: "phone", - placeholder: "Please enter phone" + placeholder: "Please enter phone", + keyboardType: .numberPad // 手机号使用数字键盘 )) + firstInputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } + secondInputView.configure(with: EPLoginInputConfig( showAreaCode: false, showCodeButton: true, isSecure: false, icon: "number", - placeholder: "Please enter verification code" + placeholder: "Please enter verification code", + keyboardType: .numberPad // 验证码使用数字键盘 )) + secondInputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } secondInputView.delegate = self actionButton.setTitle("Login", for: .normal) @@ -190,15 +239,24 @@ class EPLoginTypesViewController: UIViewController { showCodeButton: false, isSecure: false, icon: "envelope", - placeholder: "Please enter email" + placeholder: "Please enter email", + keyboardType: .emailAddress // Email 使用邮箱键盘 )) + firstInputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } + secondInputView.configure(with: EPLoginInputConfig( showAreaCode: false, showCodeButton: true, isSecure: false, icon: "number", - placeholder: "Please enter verification code" + placeholder: "Please enter verification code", + keyboardType: .numberPad // 验证码使用数字键盘 )) + secondInputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } secondInputView.delegate = self // 添加第三个输入框 @@ -212,15 +270,24 @@ class EPLoginTypesViewController: UIViewController { showCodeButton: false, isSecure: false, icon: "phone", - placeholder: "Please enter phone" + placeholder: "Please enter phone", + keyboardType: .numberPad // 手机号使用数字键盘 )) + firstInputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } + secondInputView.configure(with: EPLoginInputConfig( showAreaCode: false, showCodeButton: true, isSecure: false, icon: "number", - placeholder: "Please enter verification code" + placeholder: "Please enter verification code", + keyboardType: .numberPad // 验证码使用数字键盘 )) + secondInputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } secondInputView.delegate = self // 添加第三个输入框 @@ -253,23 +320,25 @@ class EPLoginTypesViewController: UIViewController { showCodeButton: false, isSecure: true, icon: EPLoginConfig.Images.iconLock, - placeholder: "6-16 Digits + English Letters" + placeholder: "6-16 Digits + English Letters", + keyboardType: .default // 密码使用默认键盘(需要字母+数字) )) + inputView.onTextChanged = { [weak self] _ in + self?.checkActionButtonStatus() + } view.addSubview(inputView) inputView.snp.makeConstraints { make in - make.centerX.equalToSuperview() + make.leading.trailing.equalTo(firstInputView) make.top.equalTo(secondInputView.snp.bottom).offset(EPLoginConfig.Layout.inputVerticalSpacing) - make.width.equalTo(EPLoginConfig.Layout.buttonWidth) - make.height.equalTo(EPLoginConfig.Layout.buttonHeight) + make.height.equalTo(EPLoginConfig.Layout.uniformHeight) } // 重新调整 actionButton 位置 actionButton.snp.remakeConstraints { make in - make.centerX.equalToSuperview() + make.leading.trailing.equalTo(firstInputView) make.top.equalTo(inputView.snp.bottom).offset(EPLoginConfig.Layout.buttonTopSpacing) - make.width.equalTo(EPLoginConfig.Layout.buttonWidth) - make.height.equalTo(EPLoginConfig.Layout.buttonHeight) + make.height.equalTo(EPLoginConfig.Layout.uniformHeight) } thirdInputView = inputView @@ -513,16 +582,21 @@ class EPLoginTypesViewController: UIViewController { return } - let type = (displayType == .phoneReset) ? 2 : 1 // 2=找回密码, 1=登录 - - loginService.sendPhoneCode(phone: phone, areaCode: "+86", type: type) { [weak self] in - DispatchQueue.main.async { - self?.secondInputView.startCountdown() - self?.showAlert("验证码已发送") - } - } failure: { [weak self] (code: Int, msg: String) in - DispatchQueue.main.async { - self?.showAlert("发送失败: \(msg)") + // 检查是否需要人机验证 + loadCaptchaWebView { [weak self] in + guard let self = self else { return } + + let type = (self.displayType == .phoneReset) ? 2 : 1 // 2=找回密码, 1=登录 + + self.loginService.sendPhoneCode(phone: phone, areaCode: "+86", type: type) { [weak self] in + DispatchQueue.main.async { + self?.secondInputView.startCountdown() + self?.showAlert("验证码已发送") + } + } failure: { [weak self] (code: Int, msg: String) in + DispatchQueue.main.async { + self?.showAlert("发送失败: \(msg)") + } } } } @@ -538,8 +612,9 @@ class EPLoginTypesViewController: UIViewController { // MARK: - UI Helpers private func showLoading(_ show: Bool) { - actionButton.isEnabled = !show if show { + actionButton.isEnabled = false + actionButton.alpha = 0.5 actionButton.setTitle("Loading...", for: .normal) } else { switch displayType { @@ -548,9 +623,36 @@ class EPLoginTypesViewController: UIViewController { case .emailReset, .phoneReset: actionButton.setTitle("Confirm", for: .normal) } + checkActionButtonStatus() } } + /// 检查并更新按钮启用状态 + private func checkActionButtonStatus() { + let isEnabled: Bool + + switch displayType { + case .id: + let hasId = !firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + let hasPassword = !secondInputView.text.isEmpty + isEnabled = hasId && hasPassword + + case .email, .phone: + let hasAccount = !firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + let hasCode = !secondInputView.text.isEmpty + isEnabled = hasAccount && hasCode + + case .emailReset, .phoneReset: + let hasAccount = !firstInputView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + let hasCode = !secondInputView.text.isEmpty + let hasPassword = !(thirdInputView?.text.isEmpty ?? true) + isEnabled = hasAccount && hasCode && hasPassword + } + + actionButton.isEnabled = isEnabled + actionButton.alpha = isEnabled ? 1.0 : 0.5 + } + private func showAlert(_ message: String, completion: (() -> Void)? = nil) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default) { _ in @@ -558,6 +660,37 @@ class EPLoginTypesViewController: UIViewController { }) present(alert, animated: true) } + + /// 加载人机验证 Captcha WebView + /// - Parameter completion: 验证成功后的回调 + private func loadCaptchaWebView(completion: @escaping () -> Void) { + guard ClientConfig.share().shouldDisplayCaptcha else { + // 不需要验证,直接执行 + completion() + return + } + + view.endEditing(true) + + let webVC = XPWebViewController(roomUID: nil) + webVC.view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width * 0.8, height: UIScreen.main.bounds.width * 1.2) + webVC.view.backgroundColor = .clear + webVC.view.layer.cornerRadius = 12 + webVC.view.layer.masksToBounds = true + webVC.isLoginStatus = false + webVC.isPush = false + webVC.hideNavigationBar() + webVC.url = URLWithType(.captchaSwitch) + + webVC.verifyCaptcha = { result in + if result { + TTPopup.dismiss() + completion() + } + } + + TTPopup.popupView(webVC.view, style: .alert) + } } // MARK: - EPLoginInputViewDelegate diff --git a/YuMi/E-P/NewLogin/Controllers/EPLoginViewController.swift b/YuMi/E-P/NewLogin/Controllers/EPLoginViewController.swift index faef022..59bf780 100644 --- a/YuMi/E-P/NewLogin/Controllers/EPLoginViewController.swift +++ b/YuMi/E-P/NewLogin/Controllers/EPLoginViewController.swift @@ -33,18 +33,18 @@ import UIKit override func viewDidLoad() { super.viewDidLoad() + navigationController?.setNavigationBarHidden(true, animated: false) setupUI() loadPolicyStatus() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - navigationController?.setNavigationBarHidden(true, animated: animated) + } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) -// navigationController?.setNavigationBarHidden(false, animated: animated) } // MARK: - Setup diff --git a/YuMi/E-P/NewLogin/Models/EPLoginBridge.swift b/YuMi/E-P/NewLogin/Models/EPLoginBridge.swift index f6e4e2a..929191a 100644 --- a/YuMi/E-P/NewLogin/Models/EPLoginBridge.swift +++ b/YuMi/E-P/NewLogin/Models/EPLoginBridge.swift @@ -18,3 +18,16 @@ func YMLocalizedString(_ key: String) -> String { return Bundle.ymLocalizedString(forKey: key) } +/// 桥接 URLType 枚举常量 +extension URLType { + static var captchaSwitch: URLType { + return URLType(rawValue: 113)! // kCaptchaSwitchPath + } +} + +/// DES 加密辅助函数 +func encryptDES(_ plainText: String) -> String { + // 直接使用加密密钥(与 ObjC 版本保持一致) + let key = "1ea53d260ecf11e7b56e00163e046a26" + return DESEncrypt.encryptUseDES(plainText, key: key) ?? plainText +} diff --git a/YuMi/E-P/NewLogin/Models/EPLoginConfig.swift b/YuMi/E-P/NewLogin/Models/EPLoginConfig.swift index 014dfae..1191fbf 100644 --- a/YuMi/E-P/NewLogin/Models/EPLoginConfig.swift +++ b/YuMi/E-P/NewLogin/Models/EPLoginConfig.swift @@ -24,6 +24,13 @@ struct EPLoginConfig { static let loginButtonSpacing: CGFloat = 24 /// 登录按钮左右边距 static let loginButtonHorizontalPadding: CGFloat = 30 + + /// 输入框/按钮统一高度 + static let uniformHeight: CGFloat = 56 + /// 输入框/按钮统一左右边距 + static let uniformHorizontalPadding: CGFloat = 29 + /// 输入框/按钮统一圆角 + static let uniformCornerRadius: CGFloat = 28 /// 标准圆角半径(按钮/输入框) static let cornerRadius: CGFloat = 23 @@ -83,13 +90,15 @@ struct EPLoginConfig { static let feedbackButtonCornerRadius: CGFloat = 10.5 /// 输入框高度 - static let inputHeight: CGFloat = 52 + static let inputHeight: CGFloat = 56 /// 输入框圆角 - static let inputCornerRadius: CGFloat = 26 + static let inputCornerRadius: CGFloat = 28 /// 输入框左右内边距 static let inputHorizontalPadding: CGFloat = 24 /// 输入框 icon 尺寸 static let inputIconSize: CGFloat = 20 + /// 输入框边框宽度 + static let inputBorderWidth: CGFloat = 1 /// 验证码按钮宽度 static let codeButtonWidth: CGFloat = 102 @@ -117,11 +126,15 @@ struct EPLoginConfig { static let iconDisabled = UIColor.gray /// 输入框颜色 - static let inputBackground = UIColor(red: 0xF3/255.0, green: 0xF5/255.0, blue: 0xFA/255.0, alpha: 1.0) + static let inputBackground = UIColor.white.withAlphaComponent(0.1) static let inputText = UIColor(red: 0x1F/255.0, green: 0x1B/255.0, blue: 0x4F/255.0, alpha: 1.0) - static let inputBorder = UIColor.lightGray.withAlphaComponent(0.3) + static let inputBorder = UIColor.white static let inputBorderFocused = UIColor.systemPurple + /// 渐变色(Login/Confirm按钮) + static let gradientStart = UIColor(red: 0xF8/255.0, green: 0x54/255.0, blue: 0xFC/255.0, alpha: 1.0) // #F854FC + static let gradientEnd = UIColor(red: 0x50/255.0, green: 0x0F/255.0, blue: 0xFF/255.0, alpha: 1.0) // #500FFF + /// 验证码按钮颜色 static let codeButtonBackground = UIColor(red: 0x91/255.0, green: 0x68/255.0, blue: 0xFA/255.0, alpha: 1.0) @@ -203,11 +216,11 @@ struct EPLoginConfig { /// Client Secret static let clientSecret = "uyzjdhds" /// Client ID - static let clientId = "1" + static let clientId = "erban-client" /// Grant Type - static let grantType = "sms_code" + static let grantType = "password" /// 版本号 - static let version = "1.0.31" + static let version = "1" /// 验证码类型:登录 static let codeTypeLogin = 1 @@ -253,6 +266,10 @@ struct EPLoginConfig { /// 图标 - 数字 static let iconNumber = "number" + /// 密码可见性图标 + static let iconPasswordSee = "icon_password_see" + static let iconPasswordUnsee = "icon_password_unsee" + /// 图标 - 返回 static let iconBack = "chevron.left" /// 图标 - 眼睛(隐藏) diff --git a/YuMi/E-P/NewLogin/Services/EPLoginService.swift b/YuMi/E-P/NewLogin/Services/EPLoginService.swift index 917b8c2..e4e0dc4 100644 --- a/YuMi/E-P/NewLogin/Services/EPLoginService.swift +++ b/YuMi/E-P/NewLogin/Services/EPLoginService.swift @@ -83,13 +83,16 @@ import Foundation completion: @escaping () -> Void, failure: @escaping (Int, String) -> Void) { + // 🔐 DES 加密邮箱 + let encryptedEmail = encryptDES(email) + Api.emailGetCode({ (data, code, msg) in if code == 200 { completion() } else { failure(Int(code), msg ?? "发送邮箱验证码失败") } - }, emailAddress: email, type: NSNumber(value: type)) + }, emailAddress: encryptedEmail, type: NSNumber(value: type)) } /// 发送手机验证码 @@ -105,10 +108,16 @@ import Foundation completion: @escaping () -> Void, failure: @escaping (Int, String) -> Void) { - // 注意:这里需要根据实际的 Api+Login 接口调用 - // 当前 Api+Login.h 中没有直接的手机验证码接口,可能需要通过其他方式 - print("[EPLoginService] sendPhoneCode - 需要确认实际的 API 接口") - failure(-1, "手机验证码接口待确认") + // 🔐 DES 加密手机号 + let encryptedPhone = encryptDES(phone) + + Api.phoneSmsCode({ (data, code, msg) in + if code == 200 { + completion() + } else { + failure(Int(code), msg ?? "发送手机验证码失败") + } + }, mobile: encryptedPhone, type: String(type), phoneAreaCode: areaCode) } // MARK: - Login Methods @@ -124,6 +133,10 @@ import Foundation completion: @escaping (AccountModel) -> Void, failure: @escaping (Int, String) -> Void) { + // 🔐 DES 加密 ID 和密码 + let encryptedId = encryptDES(id) + let encryptedPassword = encryptDES(password) + Api.login(password: { [weak self] (data, code, msg) in self?.parseAndSaveAccount( data: data, @@ -133,8 +146,8 @@ import Foundation failure(errorCode, msg ?? "登录失败") }) }, - phone: id, - password: password, + phone: encryptedId, + password: encryptedPassword, client_secret: clientSecret, version: version, client_id: clientId, @@ -152,6 +165,9 @@ import Foundation completion: @escaping (AccountModel) -> Void, failure: @escaping (Int, String) -> Void) { + // 🔐 DES 加密邮箱 + let encryptedEmail = encryptDES(email) + Api.login(code: { [weak self] (data, code, msg) in self?.parseAndSaveAccount( data: data, @@ -161,7 +177,7 @@ import Foundation failure(errorCode, msg ?? "登录失败") }) }, - email: email, + email: encryptedEmail, code: code, client_secret: clientSecret, version: version, @@ -182,6 +198,9 @@ import Foundation completion: @escaping (AccountModel) -> Void, failure: @escaping (Int, String) -> Void) { + // 🔐 DES 加密手机号 + let encryptedPhone = encryptDES(phone) + Api.login(code: { [weak self] (data, code, msg) in self?.parseAndSaveAccount( data: data, @@ -191,7 +210,7 @@ import Foundation failure(errorCode, msg ?? "登录失败") }) }, - phone: phone, + phone: encryptedPhone, code: code, client_secret: clientSecret, version: version, @@ -215,13 +234,17 @@ import Foundation completion: @escaping () -> Void, failure: @escaping (Int, String) -> Void) { + // 🔐 DES 加密邮箱和新密码 + let encryptedEmail = encryptDES(email) + let encryptedPassword = encryptDES(newPassword) + Api.resetPassword(email: { (data, code, msg) in if code == 200 { completion() } else { failure(Int(code), msg ?? "重置密码失败") } - }, email: email, newPwd: newPassword, code: code) + }, email: encryptedEmail, newPwd: encryptedPassword, code: code) } /// 手机号重置密码 @@ -239,13 +262,17 @@ import Foundation completion: @escaping () -> Void, failure: @escaping (Int, String) -> Void) { + // 🔐 DES 加密手机号和新密码 + let encryptedPhone = encryptDES(phone) + let encryptedPassword = encryptDES(newPassword) + Api.resetPassword(phone: { (data, code, msg) in if code == 200 { completion() } else { failure(Int(code), msg ?? "重置密码失败") } - }, phone: phone, newPwd: newPassword, smsCode: code, phoneAreaCode: areaCode) + }, phone: encryptedPhone, newPwd: encryptedPassword, smsCode: code, phoneAreaCode: areaCode) } // MARK: - Phone Quick Login (保留接口) diff --git a/YuMi/E-P/NewLogin/Views/EPLoginInputView.swift b/YuMi/E-P/NewLogin/Views/EPLoginInputView.swift index 0a7c64d..67d1256 100644 --- a/YuMi/E-P/NewLogin/Views/EPLoginInputView.swift +++ b/YuMi/E-P/NewLogin/Views/EPLoginInputView.swift @@ -16,6 +16,7 @@ struct EPLoginInputConfig { var isSecure: Bool = false var icon: String? var placeholder: String + var keyboardType: UIKeyboardType = .default } /// 输入框代理 @@ -31,6 +32,9 @@ class EPLoginInputView: UIView { weak var delegate: EPLoginInputViewDelegate? + /// 输入内容变化回调 + var onTextChanged: ((String) -> Void)? + private let stackView = UIStackView() // 区号区域 @@ -82,6 +86,8 @@ class EPLoginInputView: UIView { private func setupUI() { backgroundColor = EPLoginConfig.Colors.inputBackground layer.cornerRadius = EPLoginConfig.Layout.inputCornerRadius + layer.borderWidth = EPLoginConfig.Layout.inputBorderWidth + layer.borderColor = EPLoginConfig.Colors.inputBorder.cgColor // Main StackView stackView.axis = .horizontal @@ -161,9 +167,10 @@ class EPLoginInputView: UIView { } // TextField - inputTextField.textColor = EPLoginConfig.Colors.inputText + inputTextField.textColor = EPLoginConfig.Colors.textLight inputTextField.font = .systemFont(ofSize: 14) - inputTextField.tintColor = EPLoginConfig.Colors.primary + inputTextField.tintColor = EPLoginConfig.Colors.textLight + inputTextField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) stackView.addArrangedSubview(inputTextField) inputTextField.snp.makeConstraints { make in @@ -171,15 +178,18 @@ class EPLoginInputView: UIView { } } + @objc private func textFieldDidChange() { + onTextChanged?(inputTextField.text ?? "") + } + private func setupEyeButton() { - eyeButton.setImage(UIImage(systemName: "eye.slash"), for: .normal) - eyeButton.setImage(UIImage(systemName: "eye"), for: .selected) - eyeButton.tintColor = EPLoginConfig.Colors.icon + eyeButton.setImage(kImage(EPLoginConfig.Images.iconPasswordUnsee), for: .normal) + eyeButton.setImage(kImage(EPLoginConfig.Images.iconPasswordSee), for: .selected) eyeButton.addTarget(self, action: #selector(handleEyeTap), for: .touchUpInside) stackView.addArrangedSubview(eyeButton) eyeButton.snp.makeConstraints { make in - make.width.equalTo(30) + make.size.equalTo(24) } } @@ -209,16 +219,17 @@ class EPLoginInputView: UIView { // 区号 areaStackView.isHidden = !config.showAreaCode - // Icon - if let iconName = config.icon { - iconImageView.image = UIImage(systemName: iconName) - iconImageView.isHidden = false - } else { - iconImageView.isHidden = true - } + // Icon - 默认隐藏,不再使用 + iconImageView.isHidden = true - // Placeholder - inputTextField.placeholder = config.placeholder + // Placeholder(60% 白色) + inputTextField.attributedPlaceholder = NSAttributedString( + string: config.placeholder, + attributes: [NSAttributedString.Key.foregroundColor: UIColor.white.withAlphaComponent(0.6)] + ) + + // 键盘类型 + inputTextField.keyboardType = config.keyboardType // 密码模式 inputTextField.isSecureTextEntry = config.isSecure diff --git a/YuMi/Modules/YMLogin/Api/Api+Login.m b/YuMi/Modules/YMLogin/Api/Api+Login.m index c2a128c..5eb5bfb 100644 --- a/YuMi/Modules/YMLogin/Api/Api+Login.m +++ b/YuMi/Modules/YMLogin/Api/Api+Login.m @@ -28,8 +28,8 @@ /// @param phone 手机号 /// @param password 验证码 + (void)loginWithPassword:(HttpRequestHelperCompletion)completion phone:(NSString *)phone password:(NSString *)password client_secret:(NSString *)client_secret version:(NSString *)version client_id:(NSString *)client_id grant_type:(NSString *)grant_type { - NSString * fang = [NSString stringFromBase64String:@"b2F1dGgvdG9rZW4="];///oauth/token - [self makeRequest:fang method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__,phone,password,client_secret,version, client_id, grant_type, nil]; + + [self makeRequest:@"oauth/token" method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__,phone,password,client_secret,version, client_id, grant_type, nil]; } /// 重置手机号登录密码 diff --git a/YuMi/YuMi-Bridging-Header.h b/YuMi/YuMi-Bridging-Header.h index 9a27ba4..b84bc5b 100644 --- a/YuMi/YuMi-Bridging-Header.h +++ b/YuMi/YuMi-Bridging-Header.h @@ -43,6 +43,7 @@ // MARK: - Utilities #import "UIImage+Utils.h" #import "NSString+Utils.h" +#import "UIView+GradientLayer.h" #import // MARK: - Login - Navigation & Web @@ -51,6 +52,9 @@ // MARK: - Login - Utilities #import "YUMIMacroUitls.h" // YMLocalizedString +#import "YUMIHtmlUrl.h" // URLWithType +#import "YUMIConstant.h" // KeyWithType, KeyType_PasswordEncode +#import "DESEncrypt.h" // DES加密工具 // MARK: - Login - Models (Phase 2 使用,先添加) #import "AccountInfoStorage.h" @@ -60,6 +64,10 @@ #import "Api+Login.h" #import "Api+Main.h" +// MARK: - Login - Captcha & Config +#import "ClientConfig.h" +#import "TTPopup.h" + // 注意: // 1. EPMomentViewController 和 EPMineViewController 直接继承 UIViewController // 2. 不继承 BaseViewController(避免 ClientConfig → PIBaseModel 依赖链)