From 9fcb9020906890ad7cfd3cd108abdd54166f2a1f Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Mon, 23 Mar 2026 23:29:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B3=A8=E5=86=8C=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- punchnet/Views/Login/LoginView.swift | 3 + .../ResetPassword/ResetPasswordModel.swift | 12 +- .../ResetPassword/ResetPasswordView.swift | 151 ++++++++++-------- 3 files changed, 94 insertions(+), 72 deletions(-) diff --git a/punchnet/Views/Login/LoginView.swift b/punchnet/Views/Login/LoginView.swift index e3e9849..607efcd 100644 --- a/punchnet/Views/Login/LoginView.swift +++ b/punchnet/Views/Login/LoginView.swift @@ -176,6 +176,9 @@ struct LoginAccountView: View { withAnimation(.spring(duration: 0.6, bounce: 0.2)) { self.appContext.appScene = .logined } + } catch let err as SDLAPIError { + self.showAlert = true + self.errorMessage = err.message } catch let err { self.showAlert = true self.errorMessage = err.localizedDescription diff --git a/punchnet/Views/ResetPassword/ResetPasswordModel.swift b/punchnet/Views/ResetPassword/ResetPasswordModel.swift index f3f9882..61456ef 100644 --- a/punchnet/Views/ResetPassword/ResetPasswordModel.swift +++ b/punchnet/Views/ResetPassword/ResetPasswordModel.swift @@ -13,15 +13,19 @@ import SwiftUI class ResetPasswordModel { enum Stage: Equatable { - case requestVerifyCode(username: String?) - case submitVerifyCode(username: String, sessionId: Int) - case resetPassword(username: String, sessionId: Int) + case requestVerifyCode + case submitVerifyCode + case resetPassword case success } - var stage: Stage = .requestVerifyCode(username: nil) + var stage: Stage = .requestVerifyCode var transitionEdge: Edge = .trailing // 默认从右进入 + // 保存内部状态 + var username: String = "" + var sessionId: Int = 0 + // 重置会话信息 struct ResetPasswordSession: Codable { let sessionId: Int diff --git a/punchnet/Views/ResetPassword/ResetPasswordView.swift b/punchnet/Views/ResetPassword/ResetPasswordView.swift index 7645bb1..e0e85d6 100644 --- a/punchnet/Views/ResetPassword/ResetPasswordView.swift +++ b/punchnet/Views/ResetPassword/ResetPasswordView.swift @@ -20,12 +20,12 @@ struct ResetPasswordRootView: View { Group { switch resetPasswordModel.stage { - case .requestVerifyCode(let username): - GetVerifyCodeView(username: username ?? "") - case .submitVerifyCode(let username, let sessionId): - SubmitVerifyCodeView(username: username, sessionId: sessionId) - case .resetPassword(let username, let sessionId): - ResetPasswordView(username: username, sessionId: sessionId) + case .requestVerifyCode: + GetVerifyCodeView() + case .submitVerifyCode: + SubmitVerifyCodeView() + case .resetPassword: + ResetPasswordView() case .success: ResetPasswordSuccessView() } @@ -67,20 +67,23 @@ struct ResetPasswordRootView: View { // MARK: - 2. 第一步:获取验证码 struct GetVerifyCodeView: View { @Environment(ResetPasswordModel.self) var resetPasswordModel - @State var username: String = "" @State private var isProcessing = false @State private var showAlert = false @State private var errorMessage = "" var body: some View { + @Bindable var model = resetPasswordModel + VStack(spacing: 24) { headerSection(title: "重置密码", subtitle: "请输入关联的邮箱来验证身份") - PunchTextField(icon: "person.crop.circle", placeholder: "邮箱", text: $username) + PunchTextField(icon: "person.crop.circle", placeholder: "邮箱", text: $model.username) .frame(width: 280) Button { - self.sendVerifyCode() + Task { @MainActor in + await self.sendVerifyCode(username: model.username) + } } label: { Text("获取验证码") .fontWeight(.medium) @@ -89,7 +92,7 @@ struct GetVerifyCodeView: View { .buttonStyle(.borderedProminent) .controlSize(.large) .frame(width: 280) - .disabled(!SDLUtil.isValidIdentifyContact(username) || isProcessing) + .disabled(!SDLUtil.isValidIdentifyContact(model.username) || isProcessing) Spacer() } @@ -100,30 +103,31 @@ struct GetVerifyCodeView: View { } // 发送验证码 - private func sendVerifyCode() { + private func sendVerifyCode(username: String) async { self.isProcessing = true - Task { @MainActor in - do { - let resetSession = try await resetPasswordModel.requestVerifyCode(username: username) - withAnimation(.spring(duration: 0.6, bounce: 0.2)) { - resetPasswordModel.stage = .submitVerifyCode(username: username, sessionId: resetSession.sessionId) - resetPasswordModel.transitionEdge = .trailing - } - } catch { - self.errorMessage = error.localizedDescription - self.showAlert = true - } + defer { self.isProcessing = false } + + do { + let resetSession = try await resetPasswordModel.requestVerifyCode(username: username) + withAnimation(.spring(duration: 0.6, bounce: 0.2)) { + self.resetPasswordModel.stage = .submitVerifyCode + self.resetPasswordModel.sessionId = resetSession.sessionId + + self.resetPasswordModel.transitionEdge = .trailing + } + } catch { + self.errorMessage = error.localizedDescription + self.showAlert = true + } } } // MARK: - 3. 第二步:验证验证码 struct SubmitVerifyCodeView: View { - @Environment(ResetPasswordModel.self) var resetPasswordModel - let username: String - let sessionId: Int + @Environment(ResetPasswordModel.self) var resetPasswordModel: ResetPasswordModel @State private var code: String = "" @State private var isProcessing = false @@ -142,13 +146,15 @@ struct SubmitVerifyCodeView: View { var body: some View { VStack(spacing: 24) { - headerSection(title: "身份验证", subtitle: "验证码已发送至 \(username)") + headerSection(title: "身份验证", subtitle: "验证码已发送至 \(self.resetPasswordModel.username)") VStack(alignment: .trailing, spacing: 16) { PunchTextField(icon: "envelope.badge", placeholder: "输入 6 位验证码", text: $code) Button(isResendEnabled ? "重新获取" : "重新获取 (\(remainingSeconds)s)") { - self.resendAction() + Task { @MainActor in + await self.resendAction(username: self.resetPasswordModel.username) + } } .buttonStyle(.link) .font(.caption) @@ -158,7 +164,9 @@ struct SubmitVerifyCodeView: View { VStack(spacing: 12) { Button { - self.submitAction() + Task { @MainActor in + await self.submitAction(sessionId: self.resetPasswordModel.sessionId) + } } label: { Text("验证并继续") .fontWeight(.medium) @@ -170,7 +178,7 @@ struct SubmitVerifyCodeView: View { Button("返回上一步") { withAnimation(.spring(duration: 0.6, bounce: 0.2)) { - self.resetPasswordModel.stage = .requestVerifyCode(username: self.username) + self.resetPasswordModel.stage = .requestVerifyCode self.resetPasswordModel.transitionEdge = .leading } } @@ -190,11 +198,9 @@ struct SubmitVerifyCodeView: View { } } - private func resendAction() { - Task { - _ = try? await resetPasswordModel.requestVerifyCode(username: username) - await startCountdown() - } + private func resendAction(username: String) async { + _ = try? await resetPasswordModel.requestVerifyCode(username: username) + await startCountdown() } private func startCountdown() async { @@ -207,31 +213,34 @@ struct SubmitVerifyCodeView: View { self.isResendEnabled = true } - private func submitAction() { + private func submitAction(sessionId: Int) async { self.isProcessing = true - Task { @MainActor in - do { - let result = try await resetPasswordModel.submitVerifyCode(sessionId: sessionId, verifyCode: code) - NSLog("reset password submit verify code result: \(result)") - withAnimation(.spring(duration: 0.6, bounce: 0.2)) { - self.resetPasswordModel.stage = .resetPassword(username: username, sessionId: sessionId) - self.resetPasswordModel.transitionEdge = .trailing - } - } catch let err as SDLAPIError { - self.errorMessage = err.message - self.showAlert = true - } + defer { self.isProcessing = false } + + do { + let result = try await resetPasswordModel.submitVerifyCode(sessionId: sessionId, verifyCode: code) + NSLog("reset password submit verify code result: \(result)") + withAnimation(.spring(duration: 0.6, bounce: 0.2)) { + self.resetPasswordModel.stage = .resetPassword + self.resetPasswordModel.transitionEdge = .trailing + } + } catch let err as SDLAPIError { + self.showAlert = true + self.errorMessage = err.message + } catch let err { + self.showAlert = true + self.errorMessage = err.localizedDescription + } } } // MARK: - 4. 第三步:重置密码 struct ResetPasswordView: View { - @Environment(ResetPasswordModel.self) var resetPasswordModel - let username: String - let sessionId: Int + @Environment(ResetPasswordModel.self) var resetPasswordModel: ResetPasswordModel + @State private var password = "" @State private var confirm = "" @State private var isProcessing = false @@ -264,7 +273,7 @@ struct ResetPasswordView: View { var body: some View { VStack(spacing: 24) { - headerSection(title: "设置新密码", subtitle: "请为账号 \(username) 设置一个强密码") + headerSection(title: "设置新密码", subtitle: "请为账号 \(self.resetPasswordModel.username) 设置一个强密码") VStack(spacing: 12) { PunchTextField(icon: "lock.shield", placeholder: "新密码 (至少8位)", text: $password, isSecure: true) @@ -280,7 +289,9 @@ struct ResetPasswordView: View { .frame(width: 280) Button { - self.handleReset() + Task { @MainActor in + await self.handleReset(sessionId: self.resetPasswordModel.sessionId) + } } label: { Text("重置密码并登录") .fontWeight(.medium) @@ -299,29 +310,31 @@ struct ResetPasswordView: View { } } - private func handleReset() { + private func handleReset(sessionId: Int) async { self.isProcessing = true - Task { @MainActor in - do { - let result = try await resetPasswordModel.resetPassword(sessionId: sessionId, newPassword: password) - print("密码重置成功: \(result)") - withAnimation(.spring(duration: 0.6, bounce: 0.2)) { - self.resetPasswordModel.stage = .success - self.resetPasswordModel.transitionEdge = .trailing - } - } catch { - self.showAlert = true - self.errorMessage = "重置失败, 请稍后重试" - } + defer { self.isProcessing = false } + + do { + let result = try await resetPasswordModel.resetPassword(sessionId: sessionId, newPassword: password) + print("密码重置成功: \(result)") + withAnimation(.spring(duration: 0.6, bounce: 0.2)) { + self.resetPasswordModel.stage = .success + self.resetPasswordModel.transitionEdge = .trailing + } + } catch { + self.showAlert = true + self.errorMessage = "重置失败, 请稍后重试" + } } } struct ResetPasswordSuccessView: View { - @Environment(\.dismiss) private var dismiss - + @Environment(AppContext.self) var appContext: AppContext + @Environment(ResetPasswordModel.self) var resetPasswordModel: ResetPasswordModel + // 动画状态 @State private var animateIcon = false @State private var animateText = false @@ -357,7 +370,9 @@ struct ResetPasswordSuccessView: View { } Button(action: { - dismiss() // 关闭重置窗口 + withAnimation(.spring(duration: 0.6, bounce: 0.2)) { + self.appContext.appScene = .login(username: self.resetPasswordModel.username) + } }) { Text("返回登录") .fontWeight(.bold)