忘记密码
This commit is contained in:
parent
4032cbd512
commit
253492b481
@ -15,4 +15,5 @@ class AppContext {
|
|||||||
init(noticePort: Int) {
|
init(noticePort: Int) {
|
||||||
self.noticePort = noticePort
|
self.noticePort = noticePort
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,45 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Observation
|
import Observation
|
||||||
|
|
||||||
|
enum ContactType {
|
||||||
|
case phone
|
||||||
|
case email
|
||||||
|
case invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AuthScreen {
|
||||||
|
case login
|
||||||
|
case resetPassword
|
||||||
|
case register
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LoginRootView: View {
|
||||||
|
@Environment(AppContext.self) var appContext: AppContext
|
||||||
|
@State private var authScreen = AuthScreen.login
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
switch self.authScreen {
|
||||||
|
case .login:
|
||||||
|
LoginView(authScreen: $authScreen)
|
||||||
|
case .resetPassword:
|
||||||
|
ResetPasswordView()
|
||||||
|
case .register:
|
||||||
|
ResetPasswordView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: self.authScreen) {
|
||||||
|
print("app auth screen changed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// 登陆页面
|
// 登陆页面
|
||||||
struct LoginView: View {
|
struct LoginView: View {
|
||||||
@Environment(UserContext.self) var userContext: UserContext
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
@State private var authMethod: AuthMethod = .account
|
@State private var authMethod: AuthMethod = .account
|
||||||
|
@Binding var authScreen: AuthScreen
|
||||||
|
|
||||||
enum AuthMethod {
|
enum AuthMethod {
|
||||||
case token
|
case token
|
||||||
@ -61,7 +96,7 @@ struct LoginView: View {
|
|||||||
case .token:
|
case .token:
|
||||||
LoginTokenView()
|
LoginTokenView()
|
||||||
case .account:
|
case .account:
|
||||||
LoginAccountView()
|
LoginAccountView(authScreen: $authScreen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,6 +107,8 @@ struct LoginView: View {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension LoginView {
|
||||||
|
|
||||||
struct LoginTokenView: View {
|
struct LoginTokenView: View {
|
||||||
@Environment(UserContext.self) var userContext: UserContext
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
@State private var token: String = ""
|
@State private var token: String = ""
|
||||||
@ -145,6 +182,8 @@ struct LoginTokenView: View {
|
|||||||
|
|
||||||
struct LoginAccountView: View {
|
struct LoginAccountView: View {
|
||||||
@Environment(UserContext.self) var userContext: UserContext
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
|
@Environment(AppContext.self) var appContext: AppContext
|
||||||
|
@Binding var authScreen: AuthScreen
|
||||||
|
|
||||||
@State private var username: String = ""
|
@State private var username: String = ""
|
||||||
@State private var password: String = ""
|
@State private var password: String = ""
|
||||||
@ -163,7 +202,8 @@ struct LoginAccountView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack(alignment: .center) {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
TextField("手机号/邮箱", text: $username)
|
TextField("手机号/邮箱", text: $username)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
.textFieldStyle(PlainTextFieldStyle())
|
.textFieldStyle(PlainTextFieldStyle())
|
||||||
@ -190,6 +230,17 @@ struct LoginAccountView: View {
|
|||||||
.padding(.top, 25)
|
.padding(.top, 25)
|
||||||
, alignment: .topLeading)
|
, alignment: .topLeading)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
self.authScreen = .resetPassword
|
||||||
|
} label: {
|
||||||
|
Text("忘记密码?")
|
||||||
|
.underline(true, color: .blue)
|
||||||
|
.font(.system(size: 14, weight: .regular))
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if self.username.isEmpty {
|
if self.username.isEmpty {
|
||||||
self.showAlert = true
|
self.showAlert = true
|
||||||
@ -248,7 +299,94 @@ struct LoginAccountView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 忘记密码
|
||||||
|
struct ResetPasswordView: View {
|
||||||
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
|
|
||||||
|
@State private var username: String = ""
|
||||||
|
|
||||||
|
@State private var showAlert = false
|
||||||
|
@State private var errorMessage = ""
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
Text("重置密码")
|
||||||
|
|
||||||
|
TextField("手机号/邮箱", text: $username)
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
|
.textFieldStyle(PlainTextFieldStyle())
|
||||||
|
.frame(width: 200, height: 25)
|
||||||
|
.background(Color.clear)
|
||||||
|
.foregroundColor(Color.black)
|
||||||
|
.overlay(
|
||||||
|
Rectangle()
|
||||||
|
.frame(height: 1)
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
.padding(.top, 25)
|
||||||
|
, alignment: .topLeading)
|
||||||
|
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Task { @MainActor in
|
||||||
|
await self.sendVerifyCode()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Text("获取验证码")
|
||||||
|
.font(.system(size: 14, weight: .regular))
|
||||||
|
.foregroundColor(.black)
|
||||||
|
.frame(width: 200, height: 35)
|
||||||
|
}
|
||||||
|
.frame(width: 200, height: 35)
|
||||||
|
.background(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255))
|
||||||
|
.cornerRadius(5.0)
|
||||||
|
}
|
||||||
|
.alert(isPresented: $showAlert) {
|
||||||
|
Alert(title: Text("提示"), message: Text(self.errorMessage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func sendVerifyCode() async {
|
||||||
|
if username.isEmpty {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "手机号/邮箱为空"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch identifyContact(username) {
|
||||||
|
case .email, .phone:
|
||||||
|
do {
|
||||||
|
let result = try await self.userContext.sendVerifyCode(username: username)
|
||||||
|
print("send verify code result: \(result)")
|
||||||
|
} catch let err {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = err.localizedDescription
|
||||||
|
}
|
||||||
|
case .invalid:
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "手机号/邮箱格式错误"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func identifyContact(_ input: String) -> ContactType {
|
||||||
|
let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
// 手机号正则(中国手机号为例,以 1 开头,11 位数字)
|
||||||
|
let phoneRegex = /^1[3-9][0-9]{9}$/
|
||||||
|
// 邮箱正则
|
||||||
|
let emailRegex = /^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$/
|
||||||
|
if trimmed.wholeMatch(of: phoneRegex) != nil {
|
||||||
|
return .phone
|
||||||
|
} else if trimmed.wholeMatch(of: emailRegex) != nil {
|
||||||
|
return .email
|
||||||
|
} else {
|
||||||
|
return .invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
LoginView()
|
// ResetPasswordView()
|
||||||
|
// .environment(UserContext())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ struct RootView: View {
|
|||||||
if userContext.isLogined {
|
if userContext.isLogined {
|
||||||
NetworkView()
|
NetworkView()
|
||||||
} else {
|
} else {
|
||||||
LoginView()
|
LoginRootView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,6 +92,16 @@ class UserContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func sendVerifyCode(username: String) async throws -> String {
|
||||||
|
var params: [String: Any] = [
|
||||||
|
"username": username
|
||||||
|
]
|
||||||
|
params.merge(baseParams) {$1}
|
||||||
|
|
||||||
|
return try await SDLAPIClient.doPost(path: "/auth/sendVerifyCode", params: params, as: String.self)
|
||||||
|
}
|
||||||
|
|
||||||
func loadCacheToken() -> String? {
|
func loadCacheToken() -> String? {
|
||||||
if let data = try? KeychainStore.shared.load(account: "token") {
|
if let data = try? KeychainStore.shared.load(account: "token") {
|
||||||
return String(data: data, encoding: .utf8)
|
return String(data: data, encoding: .utf8)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user