// // ResetPasswordView.swift // punchnet // // Created by 安礼成 on 2026/3/9. // import SwiftUI import Observation // MARK: - 注册根视图 struct RegisterRootView: View { @State private var registerModel = RegisterModel() var body: some View { ZStack { // 背景毛玻璃 VisualEffectView(material: .underWindowBackground, blendingMode: .behindWindow) .ignoresSafeArea() Group { switch registerModel.stage { case .requestVerifyCode: RegisterRequestVerifyCodeView() case .submitVerifyCode(let username): RegisterSubmitVerifyCodeView(username: username) case .setPassword(let username): RegisterSetPasswordView(username: username) } } .transition(.asymmetric(insertion: .move(edge: .trailing).combined(with: .opacity), removal: .move(edge: .leading).combined(with: .opacity))) } .environment(registerModel) .frame(width: 400, height: 450) } } // MARK: - 封装的输入框组件 struct PunchTextField: View { let icon: String let placeholder: String @Binding var text: String var isSecure: Bool = false var isDisabled: Bool = false var body: some View { HStack(spacing: 12) { Image(systemName: icon) .foregroundColor(.secondary) .frame(width: 20) if isSecure { SecureField(placeholder, text: $text) .textFieldStyle(.plain) } else { TextField(placeholder, text: $text) .textFieldStyle(.plain) .disabled(isDisabled) } } .padding(10) .background(Color.primary.opacity(isDisabled ? 0.02 : 0.05)) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(Color.primary.opacity(0.1), lineWidth: 1) ) } } // MARK: - 第一步:获取验证码 struct RegisterRequestVerifyCodeView: View { @Environment(RegisterModel.self) var registerModel @State private var username: String = "" @State private var isProcessing = false // 错误提示 @State private var showAlert: Bool = false @State private var errorMessage: String = "" var body: some View { VStack(spacing: 24) { headerSection(title: "创建个人网络", subtitle: "输入手机号或邮箱开始注册") VStack(spacing: 16) { PunchTextField(icon: "person.crop.circle", placeholder: "手机号 / 邮箱", text: $username) } .frame(width: 280) Button(action: { self.requestVerifyCode() }) { if isProcessing { ProgressView() .controlSize(.small) } else { Text("获取验证码") .fontWeight(.medium) .frame(maxWidth: .infinity) } } .buttonStyle(.borderedProminent) .controlSize(.large) .frame(width: 280) .disabled(username.isEmpty || isProcessing) Spacer() } .padding(40) .alert(isPresented: $showAlert) { Alert(title: Text("提示"), message: Text(self.errorMessage)) } } private func requestVerifyCode() { self.isProcessing = true Task { @MainActor in if username.isEmpty { self.showAlert = true self.errorMessage = "手机号/邮箱为空" self.isProcessing = false return } switch SDLUtil.identifyContact(username) { case .email, .phone: do { let result = try await self.registerModel.requestVerifyCode(username: username) print("send verify code result: \(result)") withAnimation(.spring()) { self.registerModel.stage = .submitVerifyCode(username: username) } } catch { self.showAlert = true self.errorMessage = error.localizedDescription } case .invalid: self.showAlert = true self.errorMessage = "手机号/邮箱格式错误" } self.isProcessing = false } } } // MARK: - 第二步:验证 struct RegisterSubmitVerifyCodeView: View { @Environment(RegisterModel.self) var registerModel let username: String @State private var code: String = "" @State private var isProcessing = false // 错误提示 @State private var showAlert: Bool = false @State private var errorMessage: String = "" var body: some View { VStack(spacing: 24) { headerSection(title: "身份验证", subtitle: "验证码已发送至 \(username)") VStack(spacing: 16) { PunchTextField(icon: "envelope.badge", placeholder: "输入 4 位验证码", text: $code) Button("没有收到?重新获取") { // Resend Logic } .buttonStyle(.link) .font(.caption) } .frame(width: 280) VStack(spacing: 12) { Button(action: { self.submitVerifyCode() }) { if isProcessing { ProgressView() .controlSize(.small) } else { Text("验证并设置密码") .fontWeight(.medium) .frame(maxWidth: .infinity) } } .buttonStyle(.borderedProminent) .controlSize(.large) Button("返回上一步") { withAnimation { registerModel.stage = .requestVerifyCode } } .buttonStyle(.plain) .foregroundColor(.secondary) } .frame(width: 280) Spacer() } .padding(40) .alert(isPresented: $showAlert) { Alert(title: Text("提示"), message: Text(errorMessage)) } } private func submitVerifyCode() { self.isProcessing = true Task { @MainActor in if self.code.isEmpty { self.showAlert = true self.errorMessage = "请输入验证码" self.isProcessing = false return } if self.code.count != 4 { self.showAlert = true self.errorMessage = "验证码错误" self.isProcessing = false return } do { let result = try await self.registerModel.submitVerifyCode(username: username, verifyCode: self.code) print("submit verify code result: \(result)") withAnimation(.spring()) { registerModel.stage = .setPassword(username: username) } } catch { self.showAlert = true self.errorMessage = error.localizedDescription } self.isProcessing = false } } } // MARK: - 第三步:设置密码 struct RegisterSetPasswordView: View { @Environment(RegisterModel.self) var registerModel let username: String @State private var password = "" @State private var confirm = "" @State private var isProcessing = false // 错误提示 @State private var showAlert: Bool = false @State private var errorMessage: String = "" var body: some View { VStack(spacing: 24) { headerSection(title: "设置安全密码", subtitle: "最后一步,请确保密码足够强大") VStack(spacing: 12) { PunchTextField(icon: "lock.shield", placeholder: "新密码", text: $password, isSecure: true) PunchTextField(icon: "lock.shield", placeholder: "确认密码", text: $confirm, isSecure: true) } .frame(width: 280) Button(action: { self.handleRegister() }) { if isProcessing { ProgressView() .controlSize(.small) } else { Text("完成注册") .fontWeight(.medium) .frame(maxWidth: .infinity) } } .buttonStyle(.borderedProminent) .controlSize(.large) .frame(width: 280) .disabled(password.isEmpty || password != confirm) Spacer() } .padding(40) .alert(isPresented: $showAlert) { Alert(title: Text("提示"), message: Text(errorMessage)) } } private func handleRegister() { self.isProcessing = true Task { @MainActor in if password.isEmpty { self.showAlert = true self.errorMessage = "请输入新密码" self.isProcessing = false return } if confirm.isEmpty || confirm != password { self.showAlert = true self.errorMessage = "两次输入的密码不一致" self.isProcessing = false 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 } self.isProcessing = false } } } // MARK: - 辅助视图 extension View { func headerSection(title: String, subtitle: String) -> some View { VStack(spacing: 8) { Image(systemName: "shield.lefthalf.filled") .font(.system(size: 42)) .foregroundStyle(.blue.gradient) Text(title) .font(.title2.bold()) Text(subtitle) .font(.subheadline) .foregroundColor(.secondary) .multilineTextAlignment(.center) } } }