101 lines
3.8 KiB
Swift
101 lines
3.8 KiB
Swift
import SwiftUI
|
|
import DesignSystem
|
|
|
|
public struct AuthScreen: View {
|
|
private let viewModel: AuthViewModel
|
|
private let onLoggedIn: () -> Void
|
|
|
|
@State private var phone = ""
|
|
@State private var code = ""
|
|
@State private var privacyAgreed = false
|
|
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
public init(viewModel: AuthViewModel, onLoggedIn: @escaping () -> Void) {
|
|
self.viewModel = viewModel
|
|
self.onLoggedIn = onLoggedIn
|
|
}
|
|
|
|
public var body: some View {
|
|
NavigationStack {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: DS.Spacing.md) {
|
|
Text("手机号登录")
|
|
.font(.title2.bold())
|
|
.foregroundStyle(DS.Color.brandDark)
|
|
Text("手机号仅用于账号绑定,不会公开展示")
|
|
.font(.footnote)
|
|
.foregroundStyle(DS.Color.faintText)
|
|
|
|
TextField("手机号", text: Binding(get: { phone }, set: { phone = digits($0, max: 11) }))
|
|
.textFieldStyle(.roundedBorder)
|
|
|
|
HStack(spacing: DS.Spacing.sm) {
|
|
TextField("验证码", text: Binding(get: { code }, set: { code = digits($0, max: 6) }))
|
|
.textFieldStyle(.roundedBorder)
|
|
Button(getCodeTitle) { viewModel.requestSmsCode(phone: phone) }
|
|
.buttonStyle(.bordered)
|
|
.disabled(!canSendCode)
|
|
}
|
|
|
|
Button { privacyAgreed.toggle() } label: {
|
|
HStack(spacing: DS.Spacing.xs) {
|
|
Image(systemName: privacyAgreed ? "checkmark.square.fill" : "square")
|
|
.foregroundStyle(privacyAgreed ? DS.Color.brandGold : DS.Color.brandMuted)
|
|
Text("我已阅读并同意《用户协议》和《隐私政策》")
|
|
.font(.caption)
|
|
.foregroundStyle(DS.Color.faintText)
|
|
}
|
|
}
|
|
.buttonStyle(.plain)
|
|
|
|
if let errorMessage = viewModel.errorMessage {
|
|
Text(errorMessage)
|
|
.font(.footnote)
|
|
.foregroundStyle(DS.Color.danger)
|
|
}
|
|
|
|
Button(viewModel.isSubmitting ? "登录中..." : "登录") {
|
|
viewModel.login(phone: phone, code: code)
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
.frame(maxWidth: .infinity)
|
|
.disabled(!canSubmit)
|
|
}
|
|
.padding(DS.Spacing.lg)
|
|
}
|
|
.navigationTitle("登录")
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button("取消") { dismiss() }
|
|
}
|
|
}
|
|
}
|
|
.onChange(of: viewModel.didLogin) { _, loggedIn in
|
|
if loggedIn { onLoggedIn() }
|
|
}
|
|
}
|
|
|
|
private var phoneValid: Bool {
|
|
phone.range(of: #"^1\d{10}$"#, options: .regularExpression) != nil
|
|
}
|
|
|
|
private var canSendCode: Bool {
|
|
phoneValid && viewModel.countdownSeconds == 0 && !viewModel.isSendingCode
|
|
}
|
|
|
|
private var canSubmit: Bool {
|
|
phoneValid && !code.isEmpty && privacyAgreed && !viewModel.isSubmitting
|
|
}
|
|
|
|
private var getCodeTitle: String {
|
|
if viewModel.countdownSeconds > 0 { return "\(viewModel.countdownSeconds)s 后重发" }
|
|
if viewModel.isSendingCode { return "发送中" }
|
|
return "获取验证码"
|
|
}
|
|
|
|
private func digits(_ string: String, max: Int) -> String {
|
|
String(string.filter(\.isNumber).prefix(max))
|
|
}
|
|
}
|