feat: 更新登录模块以支持验证码和渐变背景

主要变更:
1. 在 EPLoginTypesViewController 中添加了渐变背景到 actionButton,提升视觉效果。
2. 实现了输入框状态检查功能,确保在输入有效信息时启用登录按钮。
3. 更新了输入框配置,支持不同类型的键盘输入(如数字键盘和邮箱键盘)。
4. 在 EPLoginService 中添加了对手机号和邮箱的 DES 加密,增强安全性。
5. 更新了 EPLoginConfig,统一输入框和按钮的样式设置。

此更新旨在提升用户体验,确保登录过程的安全性和流畅性。
This commit is contained in:
edwinQQQ
2025-10-13 17:49:09 +08:00
parent 809cc44ca5
commit 02a8335d70
8 changed files with 287 additions and 78 deletions

View File

@@ -27,6 +27,8 @@ class EPLoginTypesViewController: UIViewController {
private let actionButton = UIButton(type: .system) private let actionButton = UIButton(type: .system)
private var forgotPasswordButton: UIButton? private var forgotPasswordButton: UIButton?
private var hasAddedGradient = false
// MARK: - Lifecycle // MARK: - Lifecycle
override func viewDidLoad() { override func viewDidLoad() {
@@ -40,6 +42,24 @@ class EPLoginTypesViewController: UIViewController {
navigationController?.setNavigationBarHidden(true, animated: animated) 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 // MARK: - Setup
private func setupUI() { private func setupUI() {
@@ -89,17 +109,16 @@ class EPLoginTypesViewController: UIViewController {
view.addSubview(secondInputView) view.addSubview(secondInputView)
firstInputView.snp.makeConstraints { make in 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.top.equalTo(titleLabel.snp.bottom).offset(EPLoginConfig.Layout.inputTitleSpacing)
make.width.equalTo(EPLoginConfig.Layout.buttonWidth) make.height.equalTo(EPLoginConfig.Layout.uniformHeight)
make.height.equalTo(EPLoginConfig.Layout.buttonHeight)
} }
secondInputView.snp.makeConstraints { make in secondInputView.snp.makeConstraints { make in
make.centerX.equalToSuperview() make.leading.trailing.equalTo(firstInputView)
make.top.equalTo(firstInputView.snp.bottom).offset(EPLoginConfig.Layout.inputVerticalSpacing) make.top.equalTo(firstInputView.snp.bottom).offset(EPLoginConfig.Layout.inputVerticalSpacing)
make.width.equalTo(EPLoginConfig.Layout.buttonWidth) make.height.equalTo(EPLoginConfig.Layout.uniformHeight)
make.height.equalTo(EPLoginConfig.Layout.buttonHeight)
} }
} }
@@ -107,16 +126,18 @@ class EPLoginTypesViewController: UIViewController {
view.addSubview(actionButton) view.addSubview(actionButton)
actionButton.setTitle("Login", for: .normal) actionButton.setTitle("Login", for: .normal)
actionButton.setTitleColor(EPLoginConfig.Colors.textLight, for: .normal) actionButton.setTitleColor(EPLoginConfig.Colors.textLight, for: .normal)
actionButton.backgroundColor = EPLoginConfig.Colors.primary actionButton.layer.cornerRadius = EPLoginConfig.Layout.uniformCornerRadius
actionButton.layer.cornerRadius = EPLoginConfig.Layout.cornerRadius
actionButton.titleLabel?.font = .systemFont(ofSize: EPLoginConfig.Layout.buttonFontSize, weight: .semibold) actionButton.titleLabel?.font = .systemFont(ofSize: EPLoginConfig.Layout.buttonFontSize, weight: .semibold)
actionButton.addTarget(self, action: #selector(handleAction), for: .touchUpInside) actionButton.addTarget(self, action: #selector(handleAction), for: .touchUpInside)
//
actionButton.isEnabled = false
actionButton.alpha = 0.5
actionButton.snp.makeConstraints { make in actionButton.snp.makeConstraints { make in
make.centerX.equalToSuperview() make.leading.trailing.equalTo(firstInputView)
make.top.equalTo(secondInputView.snp.bottom).offset(EPLoginConfig.Layout.buttonTopSpacing) make.top.equalTo(secondInputView.snp.bottom).offset(EPLoginConfig.Layout.buttonTopSpacing)
make.width.equalTo(EPLoginConfig.Layout.buttonWidth) make.height.equalTo(EPLoginConfig.Layout.uniformHeight)
make.height.equalTo(EPLoginConfig.Layout.buttonHeight)
} }
} }
@@ -130,16 +151,26 @@ class EPLoginTypesViewController: UIViewController {
showAreaCode: false, showAreaCode: false,
showCodeButton: false, showCodeButton: false,
isSecure: false, isSecure: false,
icon: "person", icon: "icon_login_id",
placeholder: "Please enter ID" placeholder: "Please enter ID",
keyboardType: .numberPad // ID 使
)) ))
firstInputView.onTextChanged = { [weak self] _ in
self?.checkActionButtonStatus()
}
secondInputView.configure(with: EPLoginInputConfig( secondInputView.configure(with: EPLoginInputConfig(
showAreaCode: false, showAreaCode: false,
showCodeButton: false, showCodeButton: false,
isSecure: true, isSecure: true,
icon: "lock", icon: "icon_login_id",
placeholder: "Please enter password" placeholder: "Please enter password",
keyboardType: .default // 使+
)) ))
secondInputView.onTextChanged = { [weak self] _ in
self?.checkActionButtonStatus()
}
actionButton.setTitle("Login", for: .normal) actionButton.setTitle("Login", for: .normal)
// //
@@ -152,15 +183,24 @@ class EPLoginTypesViewController: UIViewController {
showCodeButton: false, showCodeButton: false,
isSecure: false, isSecure: false,
icon: "envelope", icon: "envelope",
placeholder: "Please enter email" placeholder: "Please enter email",
keyboardType: .emailAddress // Email 使
)) ))
firstInputView.onTextChanged = { [weak self] _ in
self?.checkActionButtonStatus()
}
secondInputView.configure(with: EPLoginInputConfig( secondInputView.configure(with: EPLoginInputConfig(
showAreaCode: false, showAreaCode: false,
showCodeButton: true, showCodeButton: true,
isSecure: false, isSecure: false,
icon: "number", icon: "number",
placeholder: "Please enter verification code" placeholder: "Please enter verification code",
keyboardType: .numberPad // 使
)) ))
secondInputView.onTextChanged = { [weak self] _ in
self?.checkActionButtonStatus()
}
secondInputView.delegate = self secondInputView.delegate = self
actionButton.setTitle("Login", for: .normal) actionButton.setTitle("Login", for: .normal)
@@ -171,15 +211,24 @@ class EPLoginTypesViewController: UIViewController {
showCodeButton: false, showCodeButton: false,
isSecure: false, isSecure: false,
icon: "phone", icon: "phone",
placeholder: "Please enter phone" placeholder: "Please enter phone",
keyboardType: .numberPad // 使
)) ))
firstInputView.onTextChanged = { [weak self] _ in
self?.checkActionButtonStatus()
}
secondInputView.configure(with: EPLoginInputConfig( secondInputView.configure(with: EPLoginInputConfig(
showAreaCode: false, showAreaCode: false,
showCodeButton: true, showCodeButton: true,
isSecure: false, isSecure: false,
icon: "number", icon: "number",
placeholder: "Please enter verification code" placeholder: "Please enter verification code",
keyboardType: .numberPad // 使
)) ))
secondInputView.onTextChanged = { [weak self] _ in
self?.checkActionButtonStatus()
}
secondInputView.delegate = self secondInputView.delegate = self
actionButton.setTitle("Login", for: .normal) actionButton.setTitle("Login", for: .normal)
@@ -190,15 +239,24 @@ class EPLoginTypesViewController: UIViewController {
showCodeButton: false, showCodeButton: false,
isSecure: false, isSecure: false,
icon: "envelope", icon: "envelope",
placeholder: "Please enter email" placeholder: "Please enter email",
keyboardType: .emailAddress // Email 使
)) ))
firstInputView.onTextChanged = { [weak self] _ in
self?.checkActionButtonStatus()
}
secondInputView.configure(with: EPLoginInputConfig( secondInputView.configure(with: EPLoginInputConfig(
showAreaCode: false, showAreaCode: false,
showCodeButton: true, showCodeButton: true,
isSecure: false, isSecure: false,
icon: "number", icon: "number",
placeholder: "Please enter verification code" placeholder: "Please enter verification code",
keyboardType: .numberPad // 使
)) ))
secondInputView.onTextChanged = { [weak self] _ in
self?.checkActionButtonStatus()
}
secondInputView.delegate = self secondInputView.delegate = self
// //
@@ -212,15 +270,24 @@ class EPLoginTypesViewController: UIViewController {
showCodeButton: false, showCodeButton: false,
isSecure: false, isSecure: false,
icon: "phone", icon: "phone",
placeholder: "Please enter phone" placeholder: "Please enter phone",
keyboardType: .numberPad // 使
)) ))
firstInputView.onTextChanged = { [weak self] _ in
self?.checkActionButtonStatus()
}
secondInputView.configure(with: EPLoginInputConfig( secondInputView.configure(with: EPLoginInputConfig(
showAreaCode: false, showAreaCode: false,
showCodeButton: true, showCodeButton: true,
isSecure: false, isSecure: false,
icon: "number", icon: "number",
placeholder: "Please enter verification code" placeholder: "Please enter verification code",
keyboardType: .numberPad // 使
)) ))
secondInputView.onTextChanged = { [weak self] _ in
self?.checkActionButtonStatus()
}
secondInputView.delegate = self secondInputView.delegate = self
// //
@@ -253,23 +320,25 @@ class EPLoginTypesViewController: UIViewController {
showCodeButton: false, showCodeButton: false,
isSecure: true, isSecure: true,
icon: EPLoginConfig.Images.iconLock, 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) view.addSubview(inputView)
inputView.snp.makeConstraints { make in inputView.snp.makeConstraints { make in
make.centerX.equalToSuperview() make.leading.trailing.equalTo(firstInputView)
make.top.equalTo(secondInputView.snp.bottom).offset(EPLoginConfig.Layout.inputVerticalSpacing) make.top.equalTo(secondInputView.snp.bottom).offset(EPLoginConfig.Layout.inputVerticalSpacing)
make.width.equalTo(EPLoginConfig.Layout.buttonWidth) make.height.equalTo(EPLoginConfig.Layout.uniformHeight)
make.height.equalTo(EPLoginConfig.Layout.buttonHeight)
} }
// actionButton // actionButton
actionButton.snp.remakeConstraints { make in actionButton.snp.remakeConstraints { make in
make.centerX.equalToSuperview() make.leading.trailing.equalTo(firstInputView)
make.top.equalTo(inputView.snp.bottom).offset(EPLoginConfig.Layout.buttonTopSpacing) make.top.equalTo(inputView.snp.bottom).offset(EPLoginConfig.Layout.buttonTopSpacing)
make.width.equalTo(EPLoginConfig.Layout.buttonWidth) make.height.equalTo(EPLoginConfig.Layout.uniformHeight)
make.height.equalTo(EPLoginConfig.Layout.buttonHeight)
} }
thirdInputView = inputView thirdInputView = inputView
@@ -513,16 +582,21 @@ class EPLoginTypesViewController: UIViewController {
return return
} }
let type = (displayType == .phoneReset) ? 2 : 1 // 2=, 1= //
loadCaptchaWebView { [weak self] in
loginService.sendPhoneCode(phone: phone, areaCode: "+86", type: type) { [weak self] in guard let self = self else { return }
DispatchQueue.main.async {
self?.secondInputView.startCountdown() let type = (self.displayType == .phoneReset) ? 2 : 1 // 2=, 1=
self?.showAlert("验证码已发送")
} self.loginService.sendPhoneCode(phone: phone, areaCode: "+86", type: type) { [weak self] in
} failure: { [weak self] (code: Int, msg: String) in DispatchQueue.main.async {
DispatchQueue.main.async { self?.secondInputView.startCountdown()
self?.showAlert("发送失败: \(msg)") 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 // MARK: - UI Helpers
private func showLoading(_ show: Bool) { private func showLoading(_ show: Bool) {
actionButton.isEnabled = !show
if show { if show {
actionButton.isEnabled = false
actionButton.alpha = 0.5
actionButton.setTitle("Loading...", for: .normal) actionButton.setTitle("Loading...", for: .normal)
} else { } else {
switch displayType { switch displayType {
@@ -548,9 +623,36 @@ class EPLoginTypesViewController: UIViewController {
case .emailReset, .phoneReset: case .emailReset, .phoneReset:
actionButton.setTitle("Confirm", for: .normal) 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) { private func showAlert(_ message: String, completion: (() -> Void)? = nil) {
let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "确定", style: .default) { _ in alert.addAction(UIAlertAction(title: "确定", style: .default) { _ in
@@ -558,6 +660,37 @@ class EPLoginTypesViewController: UIViewController {
}) })
present(alert, animated: true) 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 // MARK: - EPLoginInputViewDelegate

View File

@@ -33,18 +33,18 @@ import UIKit
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false)
setupUI() setupUI()
loadPolicyStatus() loadPolicyStatus()
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
} }
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated) super.viewWillDisappear(animated)
// navigationController?.setNavigationBarHidden(false, animated: animated)
} }
// MARK: - Setup // MARK: - Setup

View File

@@ -18,3 +18,16 @@ func YMLocalizedString(_ key: String) -> String {
return Bundle.ymLocalizedString(forKey: key) 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
}

View File

@@ -24,6 +24,13 @@ struct EPLoginConfig {
static let loginButtonSpacing: CGFloat = 24 static let loginButtonSpacing: CGFloat = 24
/// ///
static let loginButtonHorizontalPadding: CGFloat = 30 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 static let cornerRadius: CGFloat = 23
@@ -83,13 +90,15 @@ struct EPLoginConfig {
static let feedbackButtonCornerRadius: CGFloat = 10.5 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 static let inputHorizontalPadding: CGFloat = 24
/// icon /// icon
static let inputIconSize: CGFloat = 20 static let inputIconSize: CGFloat = 20
///
static let inputBorderWidth: CGFloat = 1
/// ///
static let codeButtonWidth: CGFloat = 102 static let codeButtonWidth: CGFloat = 102
@@ -117,11 +126,15 @@ struct EPLoginConfig {
static let iconDisabled = UIColor.gray 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 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 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) 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 /// Client Secret
static let clientSecret = "uyzjdhds" static let clientSecret = "uyzjdhds"
/// Client ID /// Client ID
static let clientId = "1" static let clientId = "erban-client"
/// Grant Type /// 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 static let codeTypeLogin = 1
@@ -253,6 +266,10 @@ struct EPLoginConfig {
/// - /// -
static let iconNumber = "number" static let iconNumber = "number"
///
static let iconPasswordSee = "icon_password_see"
static let iconPasswordUnsee = "icon_password_unsee"
/// - /// -
static let iconBack = "chevron.left" static let iconBack = "chevron.left"
/// - /// -

View File

@@ -83,13 +83,16 @@ import Foundation
completion: @escaping () -> Void, completion: @escaping () -> Void,
failure: @escaping (Int, String) -> Void) { failure: @escaping (Int, String) -> Void) {
// 🔐 DES
let encryptedEmail = encryptDES(email)
Api.emailGetCode({ (data, code, msg) in Api.emailGetCode({ (data, code, msg) in
if code == 200 { if code == 200 {
completion() completion()
} else { } else {
failure(Int(code), msg ?? "发送邮箱验证码失败") 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, completion: @escaping () -> Void,
failure: @escaping (Int, String) -> Void) { failure: @escaping (Int, String) -> Void) {
// Api+Login // 🔐 DES
// Api+Login.h let encryptedPhone = encryptDES(phone)
print("[EPLoginService] sendPhoneCode - 需要确认实际的 API 接口")
failure(-1, "手机验证码接口待确认") 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 // MARK: - Login Methods
@@ -124,6 +133,10 @@ import Foundation
completion: @escaping (AccountModel) -> Void, completion: @escaping (AccountModel) -> Void,
failure: @escaping (Int, String) -> 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 Api.login(password: { [weak self] (data, code, msg) in
self?.parseAndSaveAccount( self?.parseAndSaveAccount(
data: data, data: data,
@@ -133,8 +146,8 @@ import Foundation
failure(errorCode, msg ?? "登录失败") failure(errorCode, msg ?? "登录失败")
}) })
}, },
phone: id, phone: encryptedId,
password: password, password: encryptedPassword,
client_secret: clientSecret, client_secret: clientSecret,
version: version, version: version,
client_id: clientId, client_id: clientId,
@@ -152,6 +165,9 @@ import Foundation
completion: @escaping (AccountModel) -> Void, completion: @escaping (AccountModel) -> Void,
failure: @escaping (Int, String) -> Void) { failure: @escaping (Int, String) -> Void) {
// 🔐 DES
let encryptedEmail = encryptDES(email)
Api.login(code: { [weak self] (data, code, msg) in Api.login(code: { [weak self] (data, code, msg) in
self?.parseAndSaveAccount( self?.parseAndSaveAccount(
data: data, data: data,
@@ -161,7 +177,7 @@ import Foundation
failure(errorCode, msg ?? "登录失败") failure(errorCode, msg ?? "登录失败")
}) })
}, },
email: email, email: encryptedEmail,
code: code, code: code,
client_secret: clientSecret, client_secret: clientSecret,
version: version, version: version,
@@ -182,6 +198,9 @@ import Foundation
completion: @escaping (AccountModel) -> Void, completion: @escaping (AccountModel) -> Void,
failure: @escaping (Int, String) -> Void) { failure: @escaping (Int, String) -> Void) {
// 🔐 DES
let encryptedPhone = encryptDES(phone)
Api.login(code: { [weak self] (data, code, msg) in Api.login(code: { [weak self] (data, code, msg) in
self?.parseAndSaveAccount( self?.parseAndSaveAccount(
data: data, data: data,
@@ -191,7 +210,7 @@ import Foundation
failure(errorCode, msg ?? "登录失败") failure(errorCode, msg ?? "登录失败")
}) })
}, },
phone: phone, phone: encryptedPhone,
code: code, code: code,
client_secret: clientSecret, client_secret: clientSecret,
version: version, version: version,
@@ -215,13 +234,17 @@ import Foundation
completion: @escaping () -> Void, completion: @escaping () -> Void,
failure: @escaping (Int, String) -> Void) { failure: @escaping (Int, String) -> Void) {
// 🔐 DES
let encryptedEmail = encryptDES(email)
let encryptedPassword = encryptDES(newPassword)
Api.resetPassword(email: { (data, code, msg) in Api.resetPassword(email: { (data, code, msg) in
if code == 200 { if code == 200 {
completion() completion()
} else { } else {
failure(Int(code), msg ?? "重置密码失败") 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, completion: @escaping () -> Void,
failure: @escaping (Int, String) -> Void) { failure: @escaping (Int, String) -> Void) {
// 🔐 DES
let encryptedPhone = encryptDES(phone)
let encryptedPassword = encryptDES(newPassword)
Api.resetPassword(phone: { (data, code, msg) in Api.resetPassword(phone: { (data, code, msg) in
if code == 200 { if code == 200 {
completion() completion()
} else { } else {
failure(Int(code), msg ?? "重置密码失败") failure(Int(code), msg ?? "重置密码失败")
} }
}, phone: phone, newPwd: newPassword, smsCode: code, phoneAreaCode: areaCode) }, phone: encryptedPhone, newPwd: encryptedPassword, smsCode: code, phoneAreaCode: areaCode)
} }
// MARK: - Phone Quick Login () // MARK: - Phone Quick Login ()

View File

@@ -16,6 +16,7 @@ struct EPLoginInputConfig {
var isSecure: Bool = false var isSecure: Bool = false
var icon: String? var icon: String?
var placeholder: String var placeholder: String
var keyboardType: UIKeyboardType = .default
} }
/// ///
@@ -31,6 +32,9 @@ class EPLoginInputView: UIView {
weak var delegate: EPLoginInputViewDelegate? weak var delegate: EPLoginInputViewDelegate?
///
var onTextChanged: ((String) -> Void)?
private let stackView = UIStackView() private let stackView = UIStackView()
// //
@@ -82,6 +86,8 @@ class EPLoginInputView: UIView {
private func setupUI() { private func setupUI() {
backgroundColor = EPLoginConfig.Colors.inputBackground backgroundColor = EPLoginConfig.Colors.inputBackground
layer.cornerRadius = EPLoginConfig.Layout.inputCornerRadius layer.cornerRadius = EPLoginConfig.Layout.inputCornerRadius
layer.borderWidth = EPLoginConfig.Layout.inputBorderWidth
layer.borderColor = EPLoginConfig.Colors.inputBorder.cgColor
// Main StackView // Main StackView
stackView.axis = .horizontal stackView.axis = .horizontal
@@ -161,9 +167,10 @@ class EPLoginInputView: UIView {
} }
// TextField // TextField
inputTextField.textColor = EPLoginConfig.Colors.inputText inputTextField.textColor = EPLoginConfig.Colors.textLight
inputTextField.font = .systemFont(ofSize: 14) 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) stackView.addArrangedSubview(inputTextField)
inputTextField.snp.makeConstraints { make in inputTextField.snp.makeConstraints { make in
@@ -171,15 +178,18 @@ class EPLoginInputView: UIView {
} }
} }
@objc private func textFieldDidChange() {
onTextChanged?(inputTextField.text ?? "")
}
private func setupEyeButton() { private func setupEyeButton() {
eyeButton.setImage(UIImage(systemName: "eye.slash"), for: .normal) eyeButton.setImage(kImage(EPLoginConfig.Images.iconPasswordUnsee), for: .normal)
eyeButton.setImage(UIImage(systemName: "eye"), for: .selected) eyeButton.setImage(kImage(EPLoginConfig.Images.iconPasswordSee), for: .selected)
eyeButton.tintColor = EPLoginConfig.Colors.icon
eyeButton.addTarget(self, action: #selector(handleEyeTap), for: .touchUpInside) eyeButton.addTarget(self, action: #selector(handleEyeTap), for: .touchUpInside)
stackView.addArrangedSubview(eyeButton) stackView.addArrangedSubview(eyeButton)
eyeButton.snp.makeConstraints { make in eyeButton.snp.makeConstraints { make in
make.width.equalTo(30) make.size.equalTo(24)
} }
} }
@@ -209,16 +219,17 @@ class EPLoginInputView: UIView {
// //
areaStackView.isHidden = !config.showAreaCode areaStackView.isHidden = !config.showAreaCode
// Icon // Icon - 使
if let iconName = config.icon { iconImageView.isHidden = true
iconImageView.image = UIImage(systemName: iconName)
iconImageView.isHidden = false
} else {
iconImageView.isHidden = true
}
// Placeholder // Placeholder60%
inputTextField.placeholder = config.placeholder inputTextField.attributedPlaceholder = NSAttributedString(
string: config.placeholder,
attributes: [NSAttributedString.Key.foregroundColor: UIColor.white.withAlphaComponent(0.6)]
)
//
inputTextField.keyboardType = config.keyboardType
// //
inputTextField.isSecureTextEntry = config.isSecure inputTextField.isSecureTextEntry = config.isSecure

View File

@@ -28,8 +28,8 @@
/// @param phone /// @param phone
/// @param password /// @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 { + (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];
} }
/// ///

View File

@@ -43,6 +43,7 @@
// MARK: - Utilities // MARK: - Utilities
#import "UIImage+Utils.h" #import "UIImage+Utils.h"
#import "NSString+Utils.h" #import "NSString+Utils.h"
#import "UIView+GradientLayer.h"
#import <MJExtension/MJExtension.h> #import <MJExtension/MJExtension.h>
// MARK: - Login - Navigation & Web // MARK: - Login - Navigation & Web
@@ -51,6 +52,9 @@
// MARK: - Login - Utilities // MARK: - Login - Utilities
#import "YUMIMacroUitls.h" // YMLocalizedString #import "YUMIMacroUitls.h" // YMLocalizedString
#import "YUMIHtmlUrl.h" // URLWithType
#import "YUMIConstant.h" // KeyWithType, KeyType_PasswordEncode
#import "DESEncrypt.h" // DES加密工具
// MARK: - Login - Models (Phase 2 使用,先添加) // MARK: - Login - Models (Phase 2 使用,先添加)
#import "AccountInfoStorage.h" #import "AccountInfoStorage.h"
@@ -60,6 +64,10 @@
#import "Api+Login.h" #import "Api+Login.h"
#import "Api+Main.h" #import "Api+Main.h"
// MARK: - Login - Captcha & Config
#import "ClientConfig.h"
#import "TTPopup.h"
// 注意: // 注意:
// 1. EPMomentViewController 和 EPMineViewController 直接继承 UIViewController // 1. EPMomentViewController 和 EPMineViewController 直接继承 UIViewController
// 2. 不继承 BaseViewController避免 ClientConfig → PIBaseModel 依赖链) // 2. 不继承 BaseViewController避免 ClientConfig → PIBaseModel 依赖链)