feat: 更新登录模块以支持验证码和渐变背景
主要变更: 1. 在 EPLoginTypesViewController 中添加了渐变背景到 actionButton,提升视觉效果。 2. 实现了输入框状态检查功能,确保在输入有效信息时启用登录按钮。 3. 更新了输入框配置,支持不同类型的键盘输入(如数字键盘和邮箱键盘)。 4. 在 EPLoginService 中添加了对手机号和邮箱的 DES 加密,增强安全性。 5. 更新了 EPLoginConfig,统一输入框和按钮的样式设置。 此更新旨在提升用户体验,确保登录过程的安全性和流畅性。
This commit is contained in:
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
@@ -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"
|
||||||
/// 图标 - 眼睛(隐藏)
|
/// 图标 - 眼睛(隐藏)
|
||||||
|
@@ -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 (保留接口)
|
||||||
|
@@ -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
|
// Placeholder(60% 白色)
|
||||||
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
|
||||||
|
@@ -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];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 重置手机号登录密码
|
/// 重置手机号登录密码
|
||||||
|
@@ -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 依赖链)
|
||||||
|
Reference in New Issue
Block a user