修改这册流程

This commit is contained in:
anlicheng 2026-03-23 23:10:15 +08:00
parent 883f9d7f64
commit c9c507974e
7 changed files with 148 additions and 80 deletions

View File

@ -17,14 +17,14 @@ class AppContext {
// //
enum AppScene { enum AppScene {
case login case login(username: String?)
case logined case logined
case register case register
case resetPassword case resetPassword
} }
// app // app
var appScene: AppScene = .login var appScene: AppScene = .login(username: nil)
init(noticePort: Int) { init(noticePort: Int) {
self.noticePort = noticePort self.noticePort = noticePort

View File

@ -12,6 +12,8 @@ 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
var username: String?
enum AuthMethod: String, CaseIterable { enum AuthMethod: String, CaseIterable {
case account = "账户登录" case account = "账户登录"
case token = "密钥认证" case token = "密钥认证"
@ -54,7 +56,7 @@ struct LoginView: View {
LoginTokenView() LoginTokenView()
.transition(.move(edge: .trailing).combined(with: .opacity)) .transition(.move(edge: .trailing).combined(with: .opacity))
} else { } else {
LoginAccountView() LoginAccountView(username: self.username ?? "")
.transition(.move(edge: .leading).combined(with: .opacity)) .transition(.move(edge: .leading).combined(with: .opacity))
} }
} }
@ -88,8 +90,8 @@ struct LoginAccountView: View {
@Environment(UserContext.self) var userContext: UserContext @Environment(UserContext.self) var userContext: UserContext
@Environment(AppContext.self) var appContext: AppContext @Environment(AppContext.self) var appContext: AppContext
@State private var username = "" @State var username: String = ""
@State private var password = "" @State private var password: String = ""
@State private var isLoading = false @State private var isLoading = false
// //

View File

@ -12,10 +12,10 @@ import SwiftUI
@Observable @Observable
class RegisterModel { class RegisterModel {
enum Stage { enum Stage: Equatable {
case requestVerifyCode(username: String?) case requestVerifyCode
case submitVerifyCode(username: String, sessionId: Int) case submitVerifyCode
case setPassword(sessionId: Int) case setPassword
case success case success
} }
@ -28,7 +28,11 @@ class RegisterModel {
} }
} }
var stage: Stage = .requestVerifyCode(username: nil) //
var username: String = ""
var sessionId: Int = 0
var stage: Stage = .requestVerifyCode
var transitionEdge: Edge = .trailing // var transitionEdge: Edge = .trailing //
private let baseParams: [String: Any] = [ private let baseParams: [String: Any] = [

View File

@ -10,6 +10,7 @@ import Observation
// MARK: - // MARK: -
struct RegisterRootView: View { struct RegisterRootView: View {
@State private var registerModel = RegisterModel() @State private var registerModel = RegisterModel()
@Environment(AppContext.self) private var appContext: AppContext
var body: some View { var body: some View {
ZStack { ZStack {
@ -19,12 +20,12 @@ struct RegisterRootView: View {
Group { Group {
switch registerModel.stage { switch registerModel.stage {
case .requestVerifyCode(let username): case .requestVerifyCode:
RegisterRequestVerifyCodeView(username: username ?? "") RegisterRequestVerifyCodeView()
case .submitVerifyCode(let username, let sessionId): case .submitVerifyCode:
RegisterSubmitVerifyCodeView(username: username, sessionId: sessionId) RegisterSubmitVerifyCodeView()
case .setPassword(let sessionId): case .setPassword:
RegisterSetPasswordView(sessionId: sessionId) RegisterSetPasswordView()
case .success: case .success:
RegisterSuccessView() RegisterSuccessView()
} }
@ -35,6 +36,34 @@ struct RegisterRootView: View {
)) ))
} }
.environment(registerModel) .environment(registerModel)
// --- 使 overlay ---
.overlay(alignment: .topLeading) {
//
switch registerModel.stage {
case .success:
EmptyView()
default:
Button(action: {
//
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .login(username: nil)
}
}) {
HStack {
Image(systemName: "chevron.left")
.font(.system(size: 16, weight: .semibold))
.padding(5)
Text("首页")
.font(.system(size: 16, weight: .regular))
}
.contentShape(Rectangle()) //
}
.buttonStyle(.plain)
.padding([.top, .leading], 16) //
.transition(.opacity) //
}
}
.frame(width: 500, height: 400) .frame(width: 500, height: 400)
} }
} }
@ -74,8 +103,7 @@ struct PunchTextField: View {
// MARK: - // MARK: -
struct RegisterRequestVerifyCodeView: View { struct RegisterRequestVerifyCodeView: View {
@Environment(RegisterModel.self) var registerModel @Environment(RegisterModel.self) var registerModel: RegisterModel
@State var username: String = ""
@State private var isProcessing = false @State private var isProcessing = false
// //
@ -83,16 +111,18 @@ struct RegisterRequestVerifyCodeView: View {
@State private var errorMessage: String = "" @State private var errorMessage: String = ""
var body: some View { var body: some View {
@Bindable var model = registerModel
VStack(spacing: 24) { VStack(spacing: 24) {
headerSection(title: "创建个人网络", subtitle: "输入邮箱开始注册") headerSection(title: "创建个人网络", subtitle: "输入邮箱开始注册")
VStack(spacing: 16) { VStack(spacing: 16) {
PunchTextField(icon: "person.crop.circle", placeholder: "邮箱", text: $username) PunchTextField(icon: "person.crop.circle", placeholder: "邮箱", text: $model.username)
} }
.frame(width: 280) .frame(width: 280)
Button(action: { Button(action: {
self.requestVerifyCode() self.requestVerifyCode(username: model.username)
}) { }) {
Text("获取验证码") Text("获取验证码")
.fontWeight(.medium) .fontWeight(.medium)
@ -101,7 +131,7 @@ struct RegisterRequestVerifyCodeView: View {
.buttonStyle(.borderedProminent) .buttonStyle(.borderedProminent)
.controlSize(.large) .controlSize(.large)
.frame(width: 280) .frame(width: 280)
.disabled(!SDLUtil.isValidIdentifyContact(username) || isProcessing) .disabled(!SDLUtil.isValidIdentifyContact(model.username) || isProcessing)
Spacer() Spacer()
} }
@ -111,7 +141,7 @@ struct RegisterRequestVerifyCodeView: View {
} }
} }
private func requestVerifyCode() { private func requestVerifyCode(username: String) {
self.isProcessing = true self.isProcessing = true
Task { @MainActor in Task { @MainActor in
if username.isEmpty { if username.isEmpty {
@ -126,7 +156,10 @@ struct RegisterRequestVerifyCodeView: View {
do { do {
let registerSession = try await self.registerModel.requestVerifyCode(username: username) let registerSession = try await self.registerModel.requestVerifyCode(username: username)
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.registerModel.stage = .submitVerifyCode(username: username, sessionId: registerSession.sessionId) self.registerModel.stage = .submitVerifyCode
self.registerModel.username = username
self.registerModel.sessionId = registerSession.sessionId
self.registerModel.transitionEdge = .trailing self.registerModel.transitionEdge = .trailing
} }
} catch let err as SDLAPIError { } catch let err as SDLAPIError {
@ -148,10 +181,7 @@ struct RegisterRequestVerifyCodeView: View {
// MARK: - // MARK: -
struct RegisterSubmitVerifyCodeView: View { struct RegisterSubmitVerifyCodeView: View {
@Environment(RegisterModel.self) var registerModel @Environment(RegisterModel.self) var registerModel: RegisterModel
let username: String
let sessionId: Int
@State private var code: String = "" @State private var code: String = ""
@State private var isProcessing = false @State private var isProcessing = false
@ -172,13 +202,15 @@ struct RegisterSubmitVerifyCodeView: View {
var body: some View { var body: some View {
VStack(spacing: 24) { VStack(spacing: 24) {
headerSection(title: "身份验证", subtitle: "验证码已发送至 \(username)") headerSection(title: "身份验证", subtitle: "验证码已发送至 \(registerModel.username)")
VStack(alignment: .trailing, spacing: 16) { VStack(alignment: .trailing, spacing: 16) {
PunchTextField(icon: "envelope.badge", placeholder: "输入 6 位验证码", text: $code) PunchTextField(icon: "envelope.badge", placeholder: "输入 6 位验证码", text: $code)
Button { Button {
self.resendVerifyCodeAction() Task { @MainActor in
await self.resendVerifyCodeAction(username: registerModel.username)
}
} label: { } label: {
if isEnabled { if isEnabled {
Text("没有收到?重新获取") Text("没有收到?重新获取")
@ -194,7 +226,9 @@ struct RegisterSubmitVerifyCodeView: View {
VStack(spacing: 12) { VStack(spacing: 12) {
Button(action: { Button(action: {
self.submitVerifyCode() Task { @MainActor in
await self.submitVerifyCode(sessionId: registerModel.sessionId)
}
}) { }) {
Text("验证并设置密码") Text("验证并设置密码")
.fontWeight(.medium) .fontWeight(.medium)
@ -206,7 +240,8 @@ struct RegisterSubmitVerifyCodeView: View {
Button("返回上一步") { Button("返回上一步") {
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.registerModel.stage = .requestVerifyCode(username: self.username) self.registerModel.stage = .requestVerifyCode
self.registerModel.transitionEdge = .leading self.registerModel.transitionEdge = .leading
} }
} }
@ -227,8 +262,7 @@ struct RegisterSubmitVerifyCodeView: View {
} }
// //
private func resendVerifyCodeAction() { private func resendVerifyCodeAction(username: String) async {
Task {
do { do {
let result = try await self.registerModel.requestVerifyCode(username: username) let result = try await self.registerModel.requestVerifyCode(username: username)
print("send verify code result: \(result)") print("send verify code result: \(result)")
@ -238,7 +272,6 @@ struct RegisterSubmitVerifyCodeView: View {
// //
await self.startCountdown() await self.startCountdown()
} }
}
// //
private func startCountdown() async { private func startCountdown() async {
@ -252,13 +285,15 @@ struct RegisterSubmitVerifyCodeView: View {
} }
// //
private func submitVerifyCode() { private func submitVerifyCode(sessionId: Int) async {
self.isProcessing = true self.isProcessing = true
Task { @MainActor in defer {
self.isProcessing = false
}
do { do {
_ = try await self.registerModel.submitVerifyCode(sessionId: sessionId, verifyCode: self.code) _ = try await self.registerModel.submitVerifyCode(sessionId: sessionId, verifyCode: self.code)
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.registerModel.stage = .setPassword(sessionId: sessionId) self.registerModel.stage = .setPassword
self.registerModel.transitionEdge = .trailing self.registerModel.transitionEdge = .trailing
} }
} catch let err as SDLAPIError { } catch let err as SDLAPIError {
@ -268,16 +303,13 @@ struct RegisterSubmitVerifyCodeView: View {
self.showAlert = true self.showAlert = true
self.errorMessage = err.localizedDescription self.errorMessage = err.localizedDescription
} }
self.isProcessing = false
}
} }
} }
// MARK: - // MARK: -
struct RegisterSetPasswordView: View { struct RegisterSetPasswordView: View {
@Environment(RegisterModel.self) var registerModel @Environment(RegisterModel.self) var registerModel: RegisterModel
let sessionId: Int
@State private var password = "" @State private var password = ""
@State private var confirm = "" @State private var confirm = ""
@State private var isProcessing = false @State private var isProcessing = false
@ -318,7 +350,9 @@ struct RegisterSetPasswordView: View {
.frame(width: 280) .frame(width: 280)
Button(action: { Button(action: {
self.handleRegister() Task { @MainActor in
await self.handleRegister(sessionId: registerModel.sessionId)
}
}) { }) {
if isProcessing { if isProcessing {
ProgressView() ProgressView()
@ -342,9 +376,11 @@ struct RegisterSetPasswordView: View {
} }
} }
private func handleRegister() { private func handleRegister(sessionId: Int) async {
self.isProcessing = true self.isProcessing = true
Task { @MainActor in defer {
self.isProcessing = false
}
do { do {
_ = try await self.registerModel.register(sessionId: sessionId, password: self.password) _ = try await self.registerModel.register(sessionId: sessionId, password: self.password)
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
@ -358,15 +394,14 @@ struct RegisterSetPasswordView: View {
self.showAlert = true self.showAlert = true
self.errorMessage = "注册失败,重稍后重试" self.errorMessage = "注册失败,重稍后重试"
} }
self.isProcessing = false
}
} }
} }
// MARK: // MARK:
struct RegisterSuccessView: View { struct RegisterSuccessView: View {
@Environment(\.dismiss) private var dismiss // @Environment(AppContext.self) private var appContext: AppContext
@Environment(RegisterModel.self) private var registerModel: RegisterModel
// MARK: - // MARK: -
@State private var animateIcon: Bool = false // @State private var animateIcon: Bool = false //
@ -410,7 +445,9 @@ struct RegisterSuccessView: View {
Button(action: { Button(action: {
// //
dismiss() withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .login(username: registerModel.username)
}
}) { }) {
Text("立即开始使用") Text("立即开始使用")
.fontWeight(.bold) .fontWeight(.bold)

View File

@ -12,7 +12,7 @@ import SwiftUI
@Observable @Observable
class ResetPasswordModel { class ResetPasswordModel {
enum Stage { enum Stage: Equatable {
case requestVerifyCode(username: String?) case requestVerifyCode(username: String?)
case submitVerifyCode(username: String, sessionId: Int) case submitVerifyCode(username: String, sessionId: Int)
case resetPassword(username: String, sessionId: Int) case resetPassword(username: String, sessionId: Int)

View File

@ -10,6 +10,7 @@ import Observation
// MARK: - 1. // MARK: - 1.
struct ResetPasswordRootView: View { struct ResetPasswordRootView: View {
@State private var resetPasswordModel = ResetPasswordModel() @State private var resetPasswordModel = ResetPasswordModel()
@Environment(AppContext.self) private var appContext: AppContext
var body: some View { var body: some View {
ZStack { ZStack {
@ -35,6 +36,30 @@ struct ResetPasswordRootView: View {
)) ))
} }
.environment(resetPasswordModel) .environment(resetPasswordModel)
.overlay(alignment: .topLeading) {
//
if resetPasswordModel.stage != .success {
Button(action: {
//
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .login(username: nil)
}
}) {
HStack {
Image(systemName: "chevron.left")
.font(.system(size: 16, weight: .semibold))
.padding(5)
Text("首页")
.font(.system(size: 16, weight: .regular))
}
.contentShape(Rectangle()) //
}
.buttonStyle(.plain)
.padding([.top, .leading], 16) //
.transition(.opacity) //
}
}
.frame(width: 500, height: 400) .frame(width: 500, height: 400)
} }
} }

View File

@ -18,7 +18,7 @@ struct RootView: View {
// //
Group { Group {
switch appContext.appScene { switch appContext.appScene {
case .login: case .login(username: let username):
LoginView() LoginView()
case .logined: case .logined:
NetworkView() NetworkView()