feat: 增强邮箱登录功能和密码恢复流程
- 更新邮箱登录相关功能,新增邮箱验证码获取和登录API端点。 - 添加AccountModel以管理用户认证信息,支持会话票据的存储和更新。 - 实现密码恢复功能,支持通过邮箱获取验证码和重置密码。 - 增加本地化支持,更新相关字符串以适应新功能。 - 引入ValidationHelper以验证邮箱和密码格式,确保用户输入的有效性。 - 更新视图以支持邮箱登录和密码恢复的用户交互。
This commit is contained in:
@@ -8,6 +8,16 @@ struct EMailLoginView: View {
|
||||
// 使用本地@State管理UI状态
|
||||
@State private var email: String = ""
|
||||
@State private var verificationCode: String = ""
|
||||
@State private var codeCountdown: Int = 0
|
||||
@State private var timer: Timer?
|
||||
|
||||
// 管理输入框焦点状态
|
||||
@FocusState private var focusedField: Field?
|
||||
|
||||
enum Field {
|
||||
case email
|
||||
case verificationCode
|
||||
}
|
||||
|
||||
// 计算登录按钮是否可用
|
||||
private var isLoginButtonEnabled: Bool {
|
||||
@@ -18,17 +28,22 @@ struct EMailLoginView: View {
|
||||
private var getCodeButtonText: String {
|
||||
if store.isCodeLoading {
|
||||
return ""
|
||||
} else if store.codeCountdown > 0 {
|
||||
return "\(store.codeCountdown)S"
|
||||
} else if codeCountdown > 0 {
|
||||
return "\(codeCountdown)S"
|
||||
} else {
|
||||
return "email_login.get_code".localized
|
||||
}
|
||||
}
|
||||
|
||||
// 计算获取验证码按钮是否可用
|
||||
private var isCodeButtonEnabled: Bool {
|
||||
return !store.isCodeLoading && codeCountdown == 0
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
// 背景图片 - 使用与登录页面相同的"bg"
|
||||
// 背景图片
|
||||
Image("bg")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
@@ -83,6 +98,7 @@ struct EMailLoginView: View {
|
||||
.keyboardType(.emailAddress)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.focused($focusedField, equals: .email)
|
||||
}
|
||||
|
||||
// 验证码输入框
|
||||
@@ -104,9 +120,13 @@ struct EMailLoginView: View {
|
||||
.foregroundColor(.white)
|
||||
.font(.system(size: 16))
|
||||
.keyboardType(.numberPad)
|
||||
.focused($focusedField, equals: .verificationCode)
|
||||
|
||||
// 获取验证码按钮
|
||||
Button(action: {
|
||||
// 立即开始倒计时
|
||||
startCountdown()
|
||||
// 发送API请求
|
||||
store.send(.getVerificationCodeTapped)
|
||||
}) {
|
||||
ZStack {
|
||||
@@ -123,10 +143,10 @@ struct EMailLoginView: View {
|
||||
.frame(width: 60, height: 36)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 18)
|
||||
.fill(Color.white.opacity(store.isCodeButtonEnabled && !email.isEmpty ? 0.2 : 0.1))
|
||||
.fill(Color.white.opacity(isCodeButtonEnabled && !email.isEmpty ? 0.2 : 0.1))
|
||||
)
|
||||
}
|
||||
.disabled(!store.isCodeButtonEnabled || email.isEmpty || store.isCodeLoading)
|
||||
.disabled(!isCodeButtonEnabled || email.isEmpty || store.isCodeLoading)
|
||||
}
|
||||
.padding(.horizontal, 24)
|
||||
}
|
||||
@@ -138,7 +158,6 @@ struct EMailLoginView: View {
|
||||
|
||||
// 登录按钮
|
||||
Button(action: {
|
||||
// 发送登录action时传递本地状态
|
||||
store.send(.loginButtonTapped(email: email, verificationCode: verificationCode))
|
||||
}) {
|
||||
ZStack {
|
||||
@@ -184,21 +203,59 @@ struct EMailLoginView: View {
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
// 初始化时同步TCA状态到本地状态
|
||||
email = store.email
|
||||
verificationCode = store.verificationCode
|
||||
// 每次进入页面都重置状态
|
||||
store.send(.resetState)
|
||||
|
||||
email = ""
|
||||
verificationCode = ""
|
||||
codeCountdown = 0
|
||||
stopCountdown()
|
||||
|
||||
#if DEBUG
|
||||
// Debug环境下,确保默认数据已加载
|
||||
if email.isEmpty {
|
||||
email = "85494536@gmail.com"
|
||||
}
|
||||
if verificationCode.isEmpty {
|
||||
verificationCode = "784544"
|
||||
}
|
||||
print("🐛 Debug模式: 默认邮箱=\(email), 默认验证码=\(verificationCode)")
|
||||
email = "exzero@126.com"
|
||||
store.send(.emailChanged(email))
|
||||
#endif
|
||||
}
|
||||
.onDisappear {
|
||||
stopCountdown()
|
||||
}
|
||||
.onChange(of: email) { newEmail in
|
||||
store.send(.emailChanged(newEmail))
|
||||
}
|
||||
.onChange(of: verificationCode) { newCode in
|
||||
store.send(.verificationCodeChanged(newCode))
|
||||
}
|
||||
.onChange(of: store.isCodeLoading) { isCodeLoading in
|
||||
// 当API请求完成且成功时,自动将焦点切换到验证码输入框
|
||||
if !isCodeLoading && store.errorMessage == nil {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
focusedField = .verificationCode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 倒计时管理
|
||||
private func startCountdown() {
|
||||
stopCountdown()
|
||||
|
||||
// 立即设置倒计时
|
||||
codeCountdown = 60
|
||||
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
|
||||
DispatchQueue.main.async {
|
||||
if codeCountdown > 0 {
|
||||
codeCountdown -= 1
|
||||
} else {
|
||||
stopCountdown()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func stopCountdown() {
|
||||
timer?.invalidate()
|
||||
timer = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,4 +268,4 @@ struct EMailLoginView: View {
|
||||
},
|
||||
onBack: {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user