重置密码
This commit is contained in:
parent
89bff2f97b
commit
b12695d5bd
@ -17,16 +17,16 @@ enum AuthScreen {
|
|||||||
struct LoginRootView: View {
|
struct LoginRootView: View {
|
||||||
@Environment(AppContext.self) var appContext: AppContext
|
@Environment(AppContext.self) var appContext: AppContext
|
||||||
@State private var authScreen = AuthScreen.login
|
@State private var authScreen = AuthScreen.login
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
switch self.authScreen {
|
switch self.authScreen {
|
||||||
case .login:
|
case .login:
|
||||||
LoginView(authScreen: $authScreen)
|
LoginView(authScreen: $authScreen)
|
||||||
case .resetPassword:
|
case .resetPassword:
|
||||||
ResetPasswordView()
|
ResetPasswordRootView()
|
||||||
case .register:
|
case .register:
|
||||||
ResetPasswordView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: self.authScreen) {
|
.onChange(of: self.authScreen) {
|
||||||
@ -41,7 +41,7 @@ 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
|
@Binding var authScreen: AuthScreen
|
||||||
|
|
||||||
enum AuthMethod {
|
enum AuthMethod {
|
||||||
case token
|
case token
|
||||||
case account
|
case account
|
||||||
@ -68,7 +68,7 @@ struct LoginView: View {
|
|||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
self.authMethod = .account
|
self.authMethod = .account
|
||||||
} label: {
|
} label: {
|
||||||
@ -84,7 +84,7 @@ struct LoginView: View {
|
|||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
}
|
}
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
switch self.authMethod {
|
switch self.authMethod {
|
||||||
case .token:
|
case .token:
|
||||||
@ -101,18 +101,103 @@ struct LoginView: View {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LoginView {
|
|
||||||
|
struct LoginTokenView: View {
|
||||||
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
|
@State private var token: String = ""
|
||||||
|
|
||||||
struct LoginTokenView: View {
|
@State private var showAlert = false
|
||||||
@Environment(UserContext.self) var userContext: UserContext
|
@State private var errorMessage = ""
|
||||||
@State private var token: String = ""
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
TextField("认证密钥", text: $token)
|
||||||
|
.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)
|
||||||
|
|
||||||
|
Rectangle()
|
||||||
|
.overlay {
|
||||||
|
Text("登陆")
|
||||||
|
.font(.system(size: 14, weight: .regular))
|
||||||
|
.foregroundColor(.black)
|
||||||
|
}
|
||||||
|
.frame(width: 120, height: 35)
|
||||||
|
.foregroundColor(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255))
|
||||||
|
.cornerRadius(5.0)
|
||||||
|
.onTapGesture {
|
||||||
|
Task {
|
||||||
|
await self.doLogin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
if let cacheToken = self.userContext.loadCacheToken() {
|
||||||
|
self.token = cacheToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行登陆操作
|
||||||
|
private func doLogin() async {
|
||||||
|
do {
|
||||||
|
try await self.userContext.loginWithToken(token: self.token)
|
||||||
|
print(self.userContext.networkSession?.accessToken)
|
||||||
|
|
||||||
|
// 保存信息到KeychainStore
|
||||||
|
// let store = KeychainStore.shared
|
||||||
|
// if let tokenData = loginResult.accessToken.data(using: .utf8) {
|
||||||
|
// try store.save(tokenData, account: self.username)
|
||||||
|
// }
|
||||||
|
|
||||||
|
} catch let err as SDLAPIError {
|
||||||
|
await MainActor.run {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = err.message
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
await MainActor.run {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "内部错误,请稍后重试"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LoginAccountView: View {
|
||||||
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
|
@Environment(AppContext.self) var appContext: AppContext
|
||||||
|
@Binding var authScreen: AuthScreen
|
||||||
|
|
||||||
|
@State private var username: String = ""
|
||||||
|
@State private var password: String = ""
|
||||||
|
|
||||||
|
@State private var showAlert = false
|
||||||
|
@State private var errorMessage = ""
|
||||||
|
|
||||||
|
struct LoginResult: Decodable {
|
||||||
|
var accessToken: String
|
||||||
|
var networkId: Int
|
||||||
|
|
||||||
@State private var showAlert = false
|
enum CodingKeys: String, CodingKey {
|
||||||
@State private var errorMessage = ""
|
case accessToken = "access_token"
|
||||||
|
case networkId = "network_id"
|
||||||
var body: some View {
|
}
|
||||||
VStack {
|
}
|
||||||
TextField("认证密钥", text: $token)
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .center) {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
TextField("手机号/邮箱", text: $username)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
.textFieldStyle(PlainTextFieldStyle())
|
.textFieldStyle(PlainTextFieldStyle())
|
||||||
.frame(width: 200, height: 25)
|
.frame(width: 200, height: 25)
|
||||||
@ -125,250 +210,90 @@ extension LoginView {
|
|||||||
.padding(.top, 25)
|
.padding(.top, 25)
|
||||||
, alignment: .topLeading)
|
, alignment: .topLeading)
|
||||||
|
|
||||||
Rectangle()
|
SecureField("密码", text: $password)
|
||||||
.overlay {
|
.multilineTextAlignment(.leading)
|
||||||
Text("登陆")
|
.textFieldStyle(PlainTextFieldStyle())
|
||||||
.font(.system(size: 14, weight: .regular))
|
.frame(width: 200, height: 25)
|
||||||
.foregroundColor(.black)
|
.background(Color.clear)
|
||||||
}
|
.foregroundColor(Color.black)
|
||||||
.frame(width: 120, height: 35)
|
|
||||||
.foregroundColor(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255))
|
|
||||||
.cornerRadius(5.0)
|
|
||||||
.onTapGesture {
|
|
||||||
Task {
|
|
||||||
await self.doLogin()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onAppear {
|
|
||||||
if let cacheToken = self.userContext.loadCacheToken() {
|
|
||||||
self.token = cacheToken
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行登陆操作
|
|
||||||
private func doLogin() async {
|
|
||||||
do {
|
|
||||||
try await self.userContext.loginWithToken(token: self.token)
|
|
||||||
print(self.userContext.networkSession?.accessToken)
|
|
||||||
|
|
||||||
// 保存信息到KeychainStore
|
|
||||||
// let store = KeychainStore.shared
|
|
||||||
// if let tokenData = loginResult.accessToken.data(using: .utf8) {
|
|
||||||
// try store.save(tokenData, account: self.username)
|
|
||||||
// }
|
|
||||||
|
|
||||||
} catch let err as SDLAPIError {
|
|
||||||
await MainActor.run {
|
|
||||||
self.showAlert = true
|
|
||||||
self.errorMessage = err.message
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
await MainActor.run {
|
|
||||||
self.showAlert = true
|
|
||||||
self.errorMessage = "内部错误,请稍后重试"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LoginAccountView: View {
|
|
||||||
@Environment(UserContext.self) var userContext: UserContext
|
|
||||||
@Environment(AppContext.self) var appContext: AppContext
|
|
||||||
@Binding var authScreen: AuthScreen
|
|
||||||
|
|
||||||
@State private var username: String = ""
|
|
||||||
@State private var password: String = ""
|
|
||||||
|
|
||||||
@State private var showAlert = false
|
|
||||||
@State private var errorMessage = ""
|
|
||||||
|
|
||||||
struct LoginResult: Decodable {
|
|
||||||
var accessToken: String
|
|
||||||
var networkId: Int
|
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
|
||||||
case accessToken = "access_token"
|
|
||||||
case networkId = "network_id"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
VStack(alignment: .center) {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
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)
|
|
||||||
|
|
||||||
SecureField("密码", text: $password)
|
|
||||||
.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 {
|
|
||||||
self.authScreen = .resetPassword
|
|
||||||
} label: {
|
|
||||||
Text("忘记密码?")
|
|
||||||
.underline(true, color: .blue)
|
|
||||||
.font(.system(size: 14, weight: .regular))
|
|
||||||
.foregroundColor(.blue)
|
|
||||||
}
|
|
||||||
.buttonStyle(.plain)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
if self.username.isEmpty {
|
|
||||||
self.showAlert = true
|
|
||||||
self.errorMessage = "账号不能为空"
|
|
||||||
} else if self.password.isEmpty {
|
|
||||||
self.showAlert = true
|
|
||||||
self.errorMessage = "密码不能为空"
|
|
||||||
} else {
|
|
||||||
Task {
|
|
||||||
await self.doLogin()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Text("登陆")
|
|
||||||
.font(.system(size: 14, weight: .regular))
|
|
||||||
.foregroundColor(.black)
|
|
||||||
.frame(width: 120, height: 35)
|
|
||||||
}
|
|
||||||
.frame(width: 120, height: 35)
|
|
||||||
.background(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255))
|
|
||||||
.cornerRadius(5.0)
|
|
||||||
}
|
|
||||||
.alert(isPresented: $showAlert) {
|
|
||||||
Alert(title: Text("错误提示"), message: Text("账号密码为空"))
|
|
||||||
}
|
|
||||||
.onAppear {
|
|
||||||
if let (cacheUsername, cachePassword) = self.userContext.loadCacheUsernameAndPassword() {
|
|
||||||
self.username = cacheUsername
|
|
||||||
self.password = cachePassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行登陆操作
|
|
||||||
private func doLogin() async {
|
|
||||||
do {
|
|
||||||
try await self.userContext.loginWithAccountAndPassword(username: self.username, password: self.password)
|
|
||||||
|
|
||||||
// 保存信息到KeychainStore
|
|
||||||
// let store = KeychainStore.shared
|
|
||||||
// if let tokenData = loginResult.accessToken.data(using: .utf8) {
|
|
||||||
// try store.save(tokenData, account: self.username)
|
|
||||||
// }
|
|
||||||
|
|
||||||
} catch let err as SDLAPIError {
|
|
||||||
await MainActor.run {
|
|
||||||
self.showAlert = true
|
|
||||||
self.errorMessage = err.message
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
await MainActor.run {
|
|
||||||
self.showAlert = true
|
|
||||||
self.errorMessage = "内部错误,请稍后重试"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 忘记密码
|
|
||||||
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(spacing: 30) {
|
|
||||||
|
|
||||||
// 标题
|
|
||||||
Text("重置密码")
|
|
||||||
.font(.system(size: 18, weight: .medium))
|
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 16) {
|
|
||||||
TextField("请输入手机号或邮箱", text: $username)
|
|
||||||
.textFieldStyle(.plain)
|
|
||||||
.frame(width: 260, height: 28)
|
|
||||||
.overlay(
|
.overlay(
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.frame(height: 1)
|
.frame(height: 1)
|
||||||
.foregroundColor(.blue),
|
.foregroundColor(.blue)
|
||||||
alignment: .bottom
|
.padding(.top, 25)
|
||||||
)
|
, alignment: .topLeading)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
self.authScreen = .resetPassword
|
||||||
|
} label: {
|
||||||
|
Text("忘记密码?")
|
||||||
|
.underline(true, color: .blue)
|
||||||
|
.font(.system(size: 14, weight: .regular))
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Task { @MainActor in
|
if self.username.isEmpty {
|
||||||
await self.sendVerifyCode()
|
self.showAlert = true
|
||||||
|
self.errorMessage = "账号不能为空"
|
||||||
|
} else if self.password.isEmpty {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "密码不能为空"
|
||||||
|
} else {
|
||||||
|
Task {
|
||||||
|
await self.doLogin()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Text("获取验证码")
|
Text("登陆")
|
||||||
.font(.system(size: 14))
|
.font(.system(size: 14, weight: .regular))
|
||||||
.foregroundColor(.black)
|
.foregroundColor(.black)
|
||||||
.frame(width: 160, height: 36)
|
.frame(width: 120, height: 35)
|
||||||
.background(Color(red: 74/255, green: 207/255, blue: 154/255))
|
|
||||||
}
|
}
|
||||||
.frame(width: 160, height: 36)
|
.frame(width: 120, height: 35)
|
||||||
punchnet/Core/SDLUtil.swift.cornerRadius(6)
|
.background(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255))
|
||||||
|
.cornerRadius(5.0)
|
||||||
Spacer()
|
|
||||||
}
|
}
|
||||||
.padding(.top, 40)
|
|
||||||
.frame(width: 400, height: 400)
|
|
||||||
.alert(isPresented: $showAlert) {
|
.alert(isPresented: $showAlert) {
|
||||||
Alert(title: Text("提示"), message: Text(self.errorMessage))
|
Alert(title: Text("错误提示"), message: Text("账号密码为空"))
|
||||||
}
|
}
|
||||||
}
|
.onAppear {
|
||||||
|
if let (cacheUsername, cachePassword) = self.userContext.loadCacheUsernameAndPassword() {
|
||||||
private func sendVerifyCode() async {
|
self.username = cacheUsername
|
||||||
if username.isEmpty {
|
self.password = cachePassword
|
||||||
self.showAlert = true
|
|
||||||
self.errorMessage = "手机号/邮箱为空"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch SDLUtil.identifyContact(username) {
|
|
||||||
case .email, .phone:
|
|
||||||
do {
|
|
||||||
let result = try await self.userContext.sendVerifyCode(username: username)
|
|
||||||
print("send verify code result: \(result)")
|
|
||||||
} catch {
|
|
||||||
self.showAlert = true
|
|
||||||
self.errorMessage = error.localizedDescription
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case .invalid:
|
|
||||||
self.showAlert = true
|
|
||||||
self.errorMessage = "手机号/邮箱格式错误"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 执行登陆操作
|
||||||
|
private func doLogin() async {
|
||||||
|
do {
|
||||||
|
try await self.userContext.loginWithAccountAndPassword(username: self.username, password: self.password)
|
||||||
|
|
||||||
|
// 保存信息到KeychainStore
|
||||||
|
// let store = KeychainStore.shared
|
||||||
|
// if let tokenData = loginResult.accessToken.data(using: .utf8) {
|
||||||
|
// try store.save(tokenData, account: self.username)
|
||||||
|
// }
|
||||||
|
|
||||||
|
} catch let err as SDLAPIError {
|
||||||
|
await MainActor.run {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = err.message
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
await MainActor.run {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "内部错误,请稍后重试"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
// ResetPasswordView()
|
// ResetPasswordView()
|
||||||
// .environment(UserContext())
|
// .environment(UserContext())
|
||||||
}
|
}
|
||||||
|
|||||||
304
punchnet/Views/Login/ResetPasswordView.swift
Normal file
304
punchnet/Views/Login/ResetPasswordView.swift
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
//
|
||||||
|
// ResetPasswordView.swift
|
||||||
|
// punchnet
|
||||||
|
//
|
||||||
|
// Created by 安礼成 on 2026/3/9.
|
||||||
|
//
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
enum ResetPasswordStage {
|
||||||
|
case getVerifyCode
|
||||||
|
case submitVerifyCode(username: String)
|
||||||
|
case resetPassword(username: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ResetPasswordRootView: View {
|
||||||
|
@State private var stage: ResetPasswordStage = .getVerifyCode
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
switch stage {
|
||||||
|
case .getVerifyCode:
|
||||||
|
GetVerifyCodeView(stage: $stage)
|
||||||
|
case .submitVerifyCode(let username):
|
||||||
|
SubmitVerifyCodeView(username: username, stage: $stage)
|
||||||
|
case .resetPassword(let username):
|
||||||
|
ResetPasswordView(stage: $stage, username: username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取验证码
|
||||||
|
struct GetVerifyCodeView: View {
|
||||||
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
|
@Binding var stage: ResetPasswordStage
|
||||||
|
|
||||||
|
@State private var username: String = ""
|
||||||
|
@State private var showAlert = false
|
||||||
|
@State private var errorMessage = ""
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 30) {
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
Text("重置密码")
|
||||||
|
.font(.system(size: 18, weight: .medium))
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
TextField("手机号/邮箱", text: $username)
|
||||||
|
.textFieldStyle(.plain)
|
||||||
|
.frame(width: 260, height: 28)
|
||||||
|
.overlay(
|
||||||
|
Rectangle()
|
||||||
|
.frame(height: 1)
|
||||||
|
.foregroundColor(.blue),
|
||||||
|
alignment: .bottom
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Task { @MainActor in
|
||||||
|
//await self.sendVerifyCode()
|
||||||
|
withAnimation {
|
||||||
|
self.stage = .submitVerifyCode(username: self.username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Text("获取验证码")
|
||||||
|
.font(.system(size: 14))
|
||||||
|
.foregroundColor(.black)
|
||||||
|
.frame(width: 160, height: 36)
|
||||||
|
.background(Color(red: 74/255, green: 207/255, blue: 154/255))
|
||||||
|
}
|
||||||
|
.frame(width: 160, height: 36)
|
||||||
|
.cornerRadius(6)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.top, 40)
|
||||||
|
.frame(width: 400, height: 400)
|
||||||
|
.alert(isPresented: $showAlert) {
|
||||||
|
Alert(title: Text("提示"), message: Text(self.errorMessage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func sendVerifyCode() async {
|
||||||
|
if username.isEmpty {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "手机号/邮箱为空"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch SDLUtil.identifyContact(username) {
|
||||||
|
case .email, .phone:
|
||||||
|
do {
|
||||||
|
let result = try await self.userContext.sendVerifyCode(username: username)
|
||||||
|
print("send verify code result: \(result)")
|
||||||
|
} catch {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = error.localizedDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
case .invalid:
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "手机号/邮箱格式错误"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入验证码
|
||||||
|
struct SubmitVerifyCodeView: View {
|
||||||
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
|
|
||||||
|
@State var username: String
|
||||||
|
@Binding var stage: ResetPasswordStage
|
||||||
|
|
||||||
|
@State private var verifiyCode: String = ""
|
||||||
|
|
||||||
|
@State private var showAlert = false
|
||||||
|
@State private var errorMessage = ""
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 30) {
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
Text("重置密码")
|
||||||
|
.font(.system(size: 18, weight: .medium))
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
TextField("手机号/邮箱", text: $username)
|
||||||
|
.textFieldStyle(.plain)
|
||||||
|
.frame(width: 260, height: 28)
|
||||||
|
.disabled(true)
|
||||||
|
.overlay(
|
||||||
|
Rectangle()
|
||||||
|
.frame(height: 1)
|
||||||
|
.foregroundColor(.blue),
|
||||||
|
alignment: .bottom
|
||||||
|
)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
TextField("验证码", text: $verifiyCode)
|
||||||
|
.textFieldStyle(.plain)
|
||||||
|
.frame(width: 260, height: 28)
|
||||||
|
.overlay(
|
||||||
|
Rectangle()
|
||||||
|
.frame(height: 1)
|
||||||
|
.foregroundColor(.blue),
|
||||||
|
alignment: .bottom
|
||||||
|
)
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Task { @MainActor in
|
||||||
|
//await self.sendVerifyCode()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Text("再次获取")
|
||||||
|
.font(.system(size: 14))
|
||||||
|
.foregroundColor(.black)
|
||||||
|
.frame(width: 160, height: 36)
|
||||||
|
.background(Color(red: 74/255, green: 207/255, blue: 154/255))
|
||||||
|
}
|
||||||
|
.frame(width: 160, height: 36)
|
||||||
|
.cornerRadius(6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Task { @MainActor in
|
||||||
|
await self.submitVerifyCode()
|
||||||
|
withAnimation {
|
||||||
|
self.stage = .resetPassword(username: username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Text("设置密码")
|
||||||
|
.font(.system(size: 14))
|
||||||
|
.foregroundColor(.black)
|
||||||
|
.frame(width: 160, height: 36)
|
||||||
|
.background(Color(red: 74/255, green: 207/255, blue: 154/255))
|
||||||
|
}
|
||||||
|
.frame(width: 160, height: 36)
|
||||||
|
.cornerRadius(6)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.top, 40)
|
||||||
|
.frame(width: 400, height: 400)
|
||||||
|
.alert(isPresented: $showAlert) {
|
||||||
|
Alert(title: Text("提示"), message: Text(self.errorMessage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func submitVerifyCode() async {
|
||||||
|
if verifiyCode.isEmpty {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "请输入验证码"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if verifiyCode.count != 4 {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "验证码错误"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let result = try await self.userContext.submitVerifyCode(username: username, verifyCode: verifiyCode)
|
||||||
|
print("submit verify code result: \(result)")
|
||||||
|
} catch {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = error.localizedDescription
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置密码
|
||||||
|
struct ResetPasswordView: View {
|
||||||
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
|
@Binding var stage: ResetPasswordStage
|
||||||
|
var username: String
|
||||||
|
|
||||||
|
@State private var password: String = ""
|
||||||
|
@State private var confirmPassword: String = ""
|
||||||
|
|
||||||
|
@State private var showAlert = false
|
||||||
|
@State private var errorMessage = ""
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 30) {
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
Text("重置密码")
|
||||||
|
.font(.system(size: 18, weight: .medium))
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
SecureField("新密码", text: $password)
|
||||||
|
.textFieldStyle(.plain)
|
||||||
|
.frame(width: 260, height: 28)
|
||||||
|
.overlay(
|
||||||
|
Rectangle()
|
||||||
|
.frame(height: 1)
|
||||||
|
.foregroundColor(.blue),
|
||||||
|
alignment: .bottom
|
||||||
|
)
|
||||||
|
|
||||||
|
SecureField("再次输入密码", text: $confirmPassword)
|
||||||
|
.textFieldStyle(.plain)
|
||||||
|
.frame(width: 260, height: 28)
|
||||||
|
.overlay(
|
||||||
|
Rectangle()
|
||||||
|
.frame(height: 1)
|
||||||
|
.foregroundColor(.blue),
|
||||||
|
alignment: .bottom
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Task { @MainActor in
|
||||||
|
await self.resetPassword()
|
||||||
|
self.stage = .resetPassword(username: username)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Text("保存")
|
||||||
|
.font(.system(size: 14))
|
||||||
|
.foregroundColor(.black)
|
||||||
|
.frame(width: 160, height: 36)
|
||||||
|
.background(Color(red: 74/255, green: 207/255, blue: 154/255))
|
||||||
|
}
|
||||||
|
.frame(width: 160, height: 36)
|
||||||
|
.cornerRadius(6)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.top, 40)
|
||||||
|
.frame(width: 400, height: 400)
|
||||||
|
.alert(isPresented: $showAlert) {
|
||||||
|
Alert(title: Text("提示"), message: Text(self.errorMessage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func resetPassword() async {
|
||||||
|
if password.isEmpty {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "请输入新密码"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if confirmPassword.isEmpty || confirmPassword != password {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = "两次输入的密码不一致"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let result = try await self.userContext.resetPassword(username: username, password: self.password)
|
||||||
|
print("send verify code result: \(result)")
|
||||||
|
} catch {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = error.localizedDescription
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -92,7 +92,6 @@ class UserContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
|
||||||
func sendVerifyCode(username: String) async throws -> String {
|
func sendVerifyCode(username: String) async throws -> String {
|
||||||
var params: [String: Any] = [
|
var params: [String: Any] = [
|
||||||
"username": username
|
"username": username
|
||||||
@ -102,6 +101,26 @@ class UserContext {
|
|||||||
return try await SDLAPIClient.doPost(path: "/auth/sendVerifyCode", params: params, as: String.self)
|
return try await SDLAPIClient.doPost(path: "/auth/sendVerifyCode", params: params, as: String.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func submitVerifyCode(username: String, verifyCode: String) async throws -> String {
|
||||||
|
var params: [String: Any] = [
|
||||||
|
"username": username,
|
||||||
|
"verify_code": verifyCode,
|
||||||
|
]
|
||||||
|
params.merge(baseParams) {$1}
|
||||||
|
|
||||||
|
return try await SDLAPIClient.doPost(path: "/auth/submitVerifyCode", params: params, as: String.self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetPassword(username: String, password: String) async throws -> String {
|
||||||
|
var params: [String: Any] = [
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
]
|
||||||
|
params.merge(baseParams) {$1}
|
||||||
|
|
||||||
|
return try await SDLAPIClient.doPost(path: "/auth/resetPassword", 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