From 702eb1e6086260144fb7f5fe0e4284b83bda2f0b Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Mon, 9 Mar 2026 23:05:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- punchnet/Views/Register/RegisterModel.swift | 56 ++++ punchnet/Views/Register/RegisterView.swift | 296 ++++++++++++++++++ .../ResetPassword/ResetPasswordModel.swift | 56 ++++ .../ResetPasswordView.swift | 49 ++- punchnet/punchnetApp.swift | 6 + 5 files changed, 435 insertions(+), 28 deletions(-) create mode 100644 punchnet/Views/Register/RegisterModel.swift create mode 100644 punchnet/Views/Register/RegisterView.swift create mode 100644 punchnet/Views/ResetPassword/ResetPasswordModel.swift rename punchnet/Views/{Login => ResetPassword}/ResetPasswordView.swift (86%) diff --git a/punchnet/Views/Register/RegisterModel.swift b/punchnet/Views/Register/RegisterModel.swift new file mode 100644 index 0000000..73c2508 --- /dev/null +++ b/punchnet/Views/Register/RegisterModel.swift @@ -0,0 +1,56 @@ +// +// LoginState.swift +// punchnet +// +// Created by 安礼成 on 2026/1/16. +// + +import Foundation +import Observation + +@Observable +class RegisterModel { + + enum Stage { + case requestVerifyCode + case submitVerifyCode(username: String) + case setPassword(username: String) + } + + var stage: Stage = .requestVerifyCode + + private let baseParams: [String: Any] = [ + "client_id": SystemConfig.getClientId(), + "mac": SystemConfig.macAddressString(mac: SystemConfig.getMacAddress()) + ] + + func requestVerifyCode(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 register(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) + } + +} diff --git a/punchnet/Views/Register/RegisterView.swift b/punchnet/Views/Register/RegisterView.swift new file mode 100644 index 0000000..3aff20d --- /dev/null +++ b/punchnet/Views/Register/RegisterView.swift @@ -0,0 +1,296 @@ +// +// ResetPasswordView.swift +// punchnet +// +// Created by 安礼成 on 2026/3/9. +// +import SwiftUI + +struct RegisterRootView: View { + @State private var registerModel: RegisterModel = RegisterModel() + + var body: some View { + Group { + switch registerModel.stage { + case .requestVerifyCode: + RegisterRequestVerifyCodeView() + case .submitVerifyCode(let username): + RegisterSubmitVerifyCodeView(username: username) + case .setPassword(username: let username): + RegisterSetPasswordView(username: username) + } + } + .environment(registerModel) + } +} + +// 获取验证码 +struct RegisterRequestVerifyCodeView: View { + @Environment(RegisterModel.self) var registerModel: RegisterModel + + @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.registerModel.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.registerModel.requestVerifyCode(username: username) + print("send verify code result: \(result)") + } catch { + self.showAlert = true + self.errorMessage = error.localizedDescription + } + + case .invalid: + self.showAlert = true + self.errorMessage = "手机号/邮箱格式错误" + } + } +} + +// 输入验证码 +struct RegisterSubmitVerifyCodeView: View { + @Environment(RegisterModel.self) var registerModel: RegisterModel + + @State var username: String + @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.registerModel.stage = .setPassword(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 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.registerModel.submitVerifyCode(username: username, verifyCode: verifiyCode) + print("submit verify code result: \(result)") + } catch { + self.showAlert = true + self.errorMessage = error.localizedDescription + } + } + +} + +// 设置密码 +struct RegisterSetPasswordView: View { + @Environment(RegisterModel.self) var registerModel: RegisterModel + 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() + } + } 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.registerModel.register(username: username, password: self.password) + print("send verify code result: \(result)") + } catch { + self.showAlert = true + self.errorMessage = error.localizedDescription + } + } + +} diff --git a/punchnet/Views/ResetPassword/ResetPasswordModel.swift b/punchnet/Views/ResetPassword/ResetPasswordModel.swift new file mode 100644 index 0000000..0e0a01a --- /dev/null +++ b/punchnet/Views/ResetPassword/ResetPasswordModel.swift @@ -0,0 +1,56 @@ +// +// LoginState.swift +// punchnet +// +// Created by 安礼成 on 2026/1/16. +// + +import Foundation +import Observation + +@Observable +class ResetPasswordModel { + + enum Stage { + case requestVerifyCode + case submitVerifyCode(username: String) + case resetPassword(username: String) + } + + var stage: Stage = .requestVerifyCode + + private let baseParams: [String: Any] = [ + "client_id": SystemConfig.getClientId(), + "mac": SystemConfig.macAddressString(mac: SystemConfig.getMacAddress()) + ] + + func requestVerifyCode(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) + } + +} diff --git a/punchnet/Views/Login/ResetPasswordView.swift b/punchnet/Views/ResetPassword/ResetPasswordView.swift similarity index 86% rename from punchnet/Views/Login/ResetPasswordView.swift rename to punchnet/Views/ResetPassword/ResetPasswordView.swift index 3a5244e..6951762 100644 --- a/punchnet/Views/Login/ResetPasswordView.swift +++ b/punchnet/Views/ResetPassword/ResetPasswordView.swift @@ -6,31 +6,27 @@ // import SwiftUI -enum ResetPasswordStage { - case getVerifyCode - case submitVerifyCode(username: String) - case resetPassword(username: String) -} - struct ResetPasswordRootView: View { - @State private var stage: ResetPasswordStage = .getVerifyCode + @State private var resetPasswordModel = ResetPasswordModel() 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) + Group { + switch resetPasswordModel.stage { + case .requestVerifyCode: + GetVerifyCodeView() + case .submitVerifyCode(let username): + SubmitVerifyCodeView(username: username) + case .resetPassword(let username): + ResetPasswordView(username: username) + } } + .environment(resetPasswordModel) } } // 获取验证码 struct GetVerifyCodeView: View { - @Environment(UserContext.self) var userContext: UserContext - @Binding var stage: ResetPasswordStage + @Environment(ResetPasswordModel.self) var resetPasswordModel: ResetPasswordModel @State private var username: String = "" @State private var showAlert = false @@ -59,7 +55,7 @@ struct GetVerifyCodeView: View { Task { @MainActor in //await self.sendVerifyCode() withAnimation { - self.stage = .submitVerifyCode(username: self.username) + self.resetPasswordModel.stage = .submitVerifyCode(username: self.username) } } } label: { @@ -91,7 +87,7 @@ struct GetVerifyCodeView: View { switch SDLUtil.identifyContact(username) { case .email, .phone: do { - let result = try await self.userContext.sendVerifyCode(username: username) + let result = try await self.resetPasswordModel.requestVerifyCode(username: username) print("send verify code result: \(result)") } catch { self.showAlert = true @@ -107,11 +103,9 @@ struct GetVerifyCodeView: View { // 输入验证码 struct SubmitVerifyCodeView: View { - @Environment(UserContext.self) var userContext: UserContext - + @Environment(ResetPasswordModel.self) var resetPasswordModel: ResetPasswordModel + @State var username: String - @Binding var stage: ResetPasswordStage - @State private var verifiyCode: String = "" @State private var showAlert = false @@ -168,7 +162,7 @@ struct SubmitVerifyCodeView: View { Task { @MainActor in await self.submitVerifyCode() withAnimation { - self.stage = .resetPassword(username: username) + self.resetPasswordModel.stage = .resetPassword(username: username) } } } label: { @@ -204,7 +198,7 @@ struct SubmitVerifyCodeView: View { } do { - let result = try await self.userContext.submitVerifyCode(username: username, verifyCode: verifiyCode) + let result = try await self.resetPasswordModel.submitVerifyCode(username: username, verifyCode: verifiyCode) print("submit verify code result: \(result)") } catch { self.showAlert = true @@ -216,8 +210,8 @@ struct SubmitVerifyCodeView: View { // 重置密码 struct ResetPasswordView: View { - @Environment(UserContext.self) var userContext: UserContext - @Binding var stage: ResetPasswordStage + @Environment(ResetPasswordModel.self) var resetPasswordModel: ResetPasswordModel + var username: String @State private var password: String = "" @@ -258,7 +252,6 @@ struct ResetPasswordView: View { Button { Task { @MainActor in await self.resetPassword() - self.stage = .resetPassword(username: username) } } label: { Text("保存") @@ -293,7 +286,7 @@ struct ResetPasswordView: View { } do { - let result = try await self.userContext.resetPassword(username: username, password: self.password) + let result = try await self.resetPasswordModel.resetPassword(username: username, password: self.password) print("send verify code result: \(result)") } catch { self.showAlert = true diff --git a/punchnet/punchnetApp.swift b/punchnet/punchnetApp.swift index e664144..8af7095 100644 --- a/punchnet/punchnetApp.swift +++ b/punchnet/punchnetApp.swift @@ -96,6 +96,12 @@ struct punchnetApp: App { } .defaultSize(width: 800, height: 500) + Window("注册", id: "register") { + ResetPasswordRootView() + .environment(self.userContext) + } + .defaultSize(width: 800, height: 500) + MenuBarExtra("punchnet", image: "logo_32") { VStack { Button(action: {