From 883f9d7f64cc2457c36fe6d5762bd3ea2f839b2c Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Mon, 23 Mar 2026 22:09:28 +0800 Subject: [PATCH] fix flow --- punchnet/Views/AppContext.swift | 11 +++ punchnet/Views/Login/LoginView.swift | 74 +++++++++++++++---- punchnet/Views/Register/RegisterView.swift | 1 + .../ResetPassword/ResetPasswordView.swift | 1 + punchnet/Views/RootView.swift | 17 ++++- .../Views/Settings/SettingsAccountView.swift | 2 +- punchnet/Views/UserContext.swift | 68 ++++++++--------- punchnet/punchnetApp.swift | 37 +++++----- 8 files changed, 139 insertions(+), 72 deletions(-) diff --git a/punchnet/Views/AppContext.swift b/punchnet/Views/AppContext.swift index b2b1e8b..2cfd4a0 100644 --- a/punchnet/Views/AppContext.swift +++ b/punchnet/Views/AppContext.swift @@ -15,6 +15,17 @@ class AppContext { // 调用 "/connect" 之后的网络信息 var networkContext: NetworkContext? + // 当前的场景 + enum AppScene { + case login + case logined + case register + case resetPassword + } + + // 当前app所处的场景 + var appScene: AppScene = .login + init(noticePort: Int) { self.noticePort = noticePort } diff --git a/punchnet/Views/Login/LoginView.swift b/punchnet/Views/Login/LoginView.swift index b818ea9..ff98382 100644 --- a/punchnet/Views/Login/LoginView.swift +++ b/punchnet/Views/Login/LoginView.swift @@ -86,12 +86,16 @@ struct LoginView: View { // MARK: - 账户登录组件 struct LoginAccountView: View { @Environment(UserContext.self) var userContext: UserContext - @Environment(\.openWindow) private var openWindow + @Environment(AppContext.self) var appContext: AppContext @State private var username = "" @State private var password = "" @State private var isLoading = false + // 错误提示 + @State private var showAlert: Bool = false + @State private var errorMessage: String = "" + var body: some View { VStack(spacing: 16) { VStack(alignment: .leading, spacing: 12) { @@ -103,14 +107,18 @@ struct LoginAccountView: View { HStack { Button("注册") { - openWindow(id: "register") + withAnimation(.spring(duration: 0.6, bounce: 0.2)) { + self.appContext.appScene = .register + } } .buttonStyle(.link) .font(.system(size: 11)) .foregroundColor(.secondary) Button("忘记密码?") { - openWindow(id: "resetPassword") + withAnimation(.spring(duration: 0.6, bounce: 0.2)) { + self.appContext.appScene = .resetPassword + } } .buttonStyle(.link) .font(.system(size: 11)) @@ -121,7 +129,11 @@ struct LoginAccountView: View { .frame(width: 280) // 蓝色主按钮 - Button(action: { self.login() }) { + Button(action: { + Task { @MainActor in + await self.login() + } + }) { HStack { if isLoading { ProgressView() @@ -140,6 +152,9 @@ struct LoginAccountView: View { .keyboardShortcut(.defaultAction) // 绑定回车键 .disabled(username.isEmpty || password.isEmpty || isLoading) } + .alert(isPresented: $showAlert) { + Alert(title: Text("提示"), message: Text(self.errorMessage)) + } .onAppear { if let (cacheUsername, cachePassword) = self.userContext.loadCacheUsernameAndPassword() { self.username = cacheUsername @@ -148,11 +163,20 @@ struct LoginAccountView: View { } } - private func login() { - isLoading = true - Task { - try? await userContext.loginWithAccountAndPassword(username: username, password: password) - isLoading = false + private func login() async { + self.isLoading = true + defer { + self.isLoading = false + } + + do { + _ = try await userContext.loginWithAccountAndPassword(username: username, password: password) + withAnimation(.spring(duration: 0.6, bounce: 0.2)) { + self.appContext.appScene = .logined + } + } catch let err { + self.showAlert = true + self.errorMessage = err.localizedDescription } } } @@ -160,16 +184,24 @@ struct LoginAccountView: View { // MARK: - 密钥登录组件 struct LoginTokenView: View { @Environment(UserContext.self) var userContext: UserContext + @Environment(AppContext.self) var appContext: AppContext + @State private var token = "" @State private var isLoading = false + // 错误提示 + @State private var showAlert: Bool = false + @State private var errorMessage: String = "" + var body: some View { VStack(spacing: 20) { CustomTextField(title: "请输入认证密钥 (Token)", text: $token, icon: "key.fill") .frame(width: 280) Button(action: { - self.login() + Task { @MainActor in + await self.login() + } }) { Text("验证并连接") .fontWeight(.medium) @@ -180,6 +212,9 @@ struct LoginTokenView: View { .frame(width: 280) .disabled(token.isEmpty || isLoading) } + .alert(isPresented: $showAlert) { + Alert(title: Text("提示"), message: Text(self.errorMessage)) + } .onAppear { if let cacheToken = self.userContext.loadCacheToken() { self.token = cacheToken @@ -187,11 +222,20 @@ struct LoginTokenView: View { } } - private func login() { - isLoading = true - Task { - try? await userContext.loginWithToken(token: token) - isLoading = false + private func login() async { + self.isLoading = true + defer { + self.isLoading = false + } + + do { + _ = try await userContext.loginWithToken(token: token) + withAnimation(.spring(duration: 0.6, bounce: 0.2)) { + self.appContext.appScene = .logined + } + } catch let err { + self.showAlert = true + self.errorMessage = err.localizedDescription } } diff --git a/punchnet/Views/Register/RegisterView.swift b/punchnet/Views/Register/RegisterView.swift index 40c2f69..27717ed 100644 --- a/punchnet/Views/Register/RegisterView.swift +++ b/punchnet/Views/Register/RegisterView.swift @@ -35,6 +35,7 @@ struct RegisterRootView: View { )) } .environment(registerModel) + .frame(width: 500, height: 400) } } diff --git a/punchnet/Views/ResetPassword/ResetPasswordView.swift b/punchnet/Views/ResetPassword/ResetPasswordView.swift index bfb1940..af60669 100644 --- a/punchnet/Views/ResetPassword/ResetPasswordView.swift +++ b/punchnet/Views/ResetPassword/ResetPasswordView.swift @@ -35,6 +35,7 @@ struct ResetPasswordRootView: View { )) } .environment(resetPasswordModel) + .frame(width: 500, height: 400) } } diff --git a/punchnet/Views/RootView.swift b/punchnet/Views/RootView.swift index 129b435..be3521d 100644 --- a/punchnet/Views/RootView.swift +++ b/punchnet/Views/RootView.swift @@ -9,18 +9,29 @@ import SwiftUI struct RootView: View { @Environment(UserContext.self) var userContext + @Environment(AppContext.self) var appContext: AppContext + @State private var updateManager = AppUpdateManager.shared var body: some View { ZStack { // 主要界面 Group { - if userContext.isLogined { - NetworkView() - } else { + switch appContext.appScene { + case .login: LoginView() + case .logined: + NetworkView() + case .register: + RegisterRootView() + case .resetPassword: + ResetPasswordRootView() } } + .transition(.asymmetric( + insertion: .move(edge: .trailing).combined(with: .opacity), + removal: .move(edge: .trailing).combined(with: .opacity) + )) // 自动更新遮罩 if updateManager.showUpdateOverlay, let info = updateManager.updateInfo { diff --git a/punchnet/Views/Settings/SettingsAccountView.swift b/punchnet/Views/Settings/SettingsAccountView.swift index ea19366..60b5262 100644 --- a/punchnet/Views/Settings/SettingsAccountView.swift +++ b/punchnet/Views/Settings/SettingsAccountView.swift @@ -18,7 +18,7 @@ struct SettingsAccountView: View { sectionHeader(title: "账户安全", icon: "shield.lefthalf.filled") VStack(spacing: 0) { - if userContext.isLogined, let loginCredit = userContext.loginCredit { + if let loginCredit = userContext.loginCredit { switch loginCredit { case .token(let token): TokenCreditView(token: token) diff --git a/punchnet/Views/UserContext.swift b/punchnet/Views/UserContext.swift index 6c9862e..5e1a0ea 100644 --- a/punchnet/Views/UserContext.swift +++ b/punchnet/Views/UserContext.swift @@ -10,7 +10,6 @@ import Observation @Observable class UserContext { - var isLogined: Bool = false var loginCredit: Credit? var networkSession: NetworkSession? @@ -63,7 +62,7 @@ class UserContext { ] @MainActor - func loginWithAccountAndPassword(username: String, password: String) async throws { + func loginWithAccountAndPassword(username: String, password: String) async throws -> Bool { var params: [String: Any] = [ "username": username, "password": password, @@ -74,16 +73,17 @@ class UserContext { self.networkSession = try await SDLAPIClient.doPost(path: "/auth/login", params: params, as: NetworkSession.self) self.loginCredit = .accountAndPasword(account: username, password: password) - self.isLogined = true // 将数据缓存到keychain if let data = "\(username):\(password)".data(using: .utf8) { try KeychainStore.shared.save(data, account: "accountAndPasword") } + + return true } @MainActor - func loginWithToken(token: String) async throws { + func loginWithToken(token: String) async throws -> Bool { var params: [String: Any] = [ "token": token, "system": SystemConfig.systemInfo, @@ -93,42 +93,43 @@ class UserContext { self.networkSession = try await SDLAPIClient.doPost(path: "/auth/token", params: params, as: NetworkSession.self) self.loginCredit = .token(token: token) - self.isLogined = true // 将数据缓存到keychain if let data = token.data(using: .utf8) { try KeychainStore.shared.save(data, account: "token") } + + return true } - 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 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 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 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? { if let data = try? KeychainStore.shared.load(account: "token") { @@ -151,7 +152,6 @@ class UserContext { // 退出登陆 func logout() async throws { try await VPNManager.shared.disableVpn() - self.isLogined = false } } diff --git a/punchnet/punchnetApp.swift b/punchnet/punchnetApp.swift index dfae0fb..5c85866 100644 --- a/punchnet/punchnetApp.swift +++ b/punchnet/punchnetApp.swift @@ -45,8 +45,7 @@ struct punchnetApp: App { var body: some Scene { WindowGroup(id: "main") { - // RootView() - SettingsView() + RootView() .navigationTitle("") .environment(self.appContext) .environment(self.userContext) @@ -80,23 +79,23 @@ struct punchnetApp: App { .windowResizability(.contentSize) .defaultPosition(.center) - Window("重置密码", id: "resetPassword") { - ResetPasswordRootView() - .environment(self.userContext) - .environment(self.appContext) - .frame(width: 500, height: 400) - } - .windowResizability(.contentSize) - .defaultPosition(.center) - - Window("注册", id: "register") { - RegisterRootView() - .environment(self.userContext) - .environment(self.appContext) - .frame(width: 500, height: 400) - } - .windowResizability(.contentSize) - .defaultPosition(.center) +// Window("重置密码", id: "resetPassword") { +// ResetPasswordRootView() +// .environment(self.userContext) +// .environment(self.appContext) +// .frame(width: 500, height: 400) +// } +// .windowResizability(.contentSize) +// .defaultPosition(.center) +// +// Window("注册", id: "register") { +// RegisterRootView() +// .environment(self.userContext) +// .environment(self.appContext) +// .frame(width: 500, height: 400) +// } +// .windowResizability(.contentSize) +// .defaultPosition(.center) // // MenuBarExtra("punchnet", image: "logo_32") { // VStack {