2026-01-19 17:36:46 +08:00

219 lines
6.8 KiB
Swift

//
// LoginView.swift
// punchnet
//
// Created by on 2026/1/15.
//
import SwiftUI
import Observation
//
struct LoginView: View {
@State private var loginMode: LoginMode = .account
enum LoginMode {
case token
case account
}
var body: some View {
VStack {
Text("PunchNet")
.font(.system(size: 16, weight: .medium))
HStack(alignment: .center, spacing: 30) {
Button {
self.loginMode = .token
} label: {
HStack {
Image("logo")
.resizable()
.clipped()
.frame(width: 25, height: 25)
Text("密钥登陆")
.foregroundColor(self.loginMode == .token ? .blue : .black)
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
Button {
self.loginMode = .account
} label: {
HStack {
Image("logo")
.resizable()
.clipped()
.frame(width: 25, height: 25)
Text("账户登陆")
.foregroundColor(self.loginMode == .account ? .blue : .black)
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
}
Group {
switch loginMode {
case .token:
LoginTokenView()
case .account:
LoginAccountView()
}
}
Spacer()
}
.frame(width: 400, height: 400)
}
}
struct LoginTokenView: View {
@Environment(UserContext.self) var userContext: UserContext
@State private var token: String = ""
var body: some View {
TextField("认证密钥", text: $token)
.multilineTextAlignment(.leading)
.textFieldStyle(PlainTextFieldStyle())
.frame(width: 200, height: 25)
.background(Color.clear)
.foregroundColor(Color.black)
.overlay(
Rectangle()
.frame(height: 1)
.foregroundColor(.blue)
.padding(.top, 25)
, alignment: .topLeading)
Rectangle()
.overlay {
Text("登陆")
.font(.system(size: 14, weight: .regular))
.foregroundColor(.black)
}
.frame(width: 120, height: 35)
.foregroundColor(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255))
.cornerRadius(5.0)
.onTapGesture {
print("call me here")
}
}
}
struct LoginAccountView: View {
@Environment(UserContext.self) var userContext: UserContext
@State private var username: String = ""
@State private var password: String = ""
@State private var showAlert = false
@State private var errorMessage = ""
struct LoginResult: Decodable {
var accessToken: String
var networkId: Int
enum CodingKeys: String, CodingKey {
case accessToken = "access_token"
case networkId = "network_id"
}
}
var body: some View {
VStack {
TextField("手机号/邮箱", text: $username)
.multilineTextAlignment(.leading)
.textFieldStyle(PlainTextFieldStyle())
.frame(width: 200, height: 25)
.background(Color.clear)
.foregroundColor(Color.black)
.overlay(
Rectangle()
.frame(height: 1)
.foregroundColor(.blue)
.padding(.top, 25)
, alignment: .topLeading)
SecureField("密码", text: $password)
.multilineTextAlignment(.leading)
.textFieldStyle(PlainTextFieldStyle())
.frame(width: 200, height: 25)
.background(Color.clear)
.foregroundColor(Color.black)
.overlay(
Rectangle()
.frame(height: 1)
.foregroundColor(.blue)
.padding(.top, 25)
, alignment: .topLeading)
Button {
if self.username.isEmpty {
self.showAlert = true
self.errorMessage = "账号不能为空"
} else if self.password.isEmpty {
self.showAlert = true
self.errorMessage = "密码不能为空"
} else {
Task {
await self.doLogin()
}
}
} label: {
Text("登陆")
.font(.system(size: 14, weight: .regular))
.foregroundColor(.black)
.frame(width: 120, height: 35)
}
.frame(width: 120, height: 35)
.background(Color(red: 74 / 255, green: 207 / 255, blue: 154 / 255))
.cornerRadius(5.0)
}
.alert(isPresented: $showAlert) {
Alert(title: Text("错误提示"), message: Text("账号密码为空"))
}
}
//
private func doLogin() async {
let clientId = SystemConfig.getClientId()
do {
let loginResult = try await SDLAPI.loginWithAccountAndPassword(clientId: clientId, username: self.username, password: self.password, as: LoginResult.self)
print(loginResult.accessToken)
// KeychainStore
let store = KeychainStore.shared
if let tokenData = loginResult.accessToken.data(using: .utf8) {
try store.save(tokenData, account: self.username)
}
await MainActor.run {
self.userContext.isLogined = true
self.userContext.loginCredit = .accountAndPasword(account: username, password: password, networkId: loginResult.networkId)
}
} catch let err as JSONRPCError {
await MainActor.run {
self.showAlert = true
self.errorMessage = err.message
}
} catch {
await MainActor.run {
self.showAlert = true
self.errorMessage = "内部错误,请稍后重试"
}
}
}
}
#Preview {
LoginView()
}