219 lines
6.8 KiB
Swift
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()
|
|
}
|