Files
e-party-iOS/yana/MVVM/ViewModel/IDLoginViewModel.swift
edwinQQQ de4428e8a1 feat: 新增设置页面及相关功能实现
- 创建SettingPage视图,包含用户信息管理、头像设置、昵称编辑等功能。
- 实现SettingViewModel,处理设置页面的业务逻辑,包括头像上传、昵称更新等。
- 添加相机和相册选择功能,支持头像更换。
- 更新MainPage和MainViewModel,添加导航逻辑以支持设置页面的访问。
- 完善本地化支持,确保多语言兼容性。
- 新增相关测试建议,确保功能完整性和用户体验。
2025-08-06 18:51:37 +08:00

195 lines
6.2 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
import Combine
// MARK: - IDLogin ViewModel
@MainActor
class IDLoginViewModel: ObservableObject {
// MARK: - Published Properties
@Published var userID: String = ""
@Published var password: String = ""
@Published var isPasswordVisible: Bool = false
@Published var isLoading: Bool = false
@Published var errorMessage: String?
@Published var showRecoverPassword: Bool = false
@Published var loginStep: LoginStep = .input
// MARK: - Ticket
@Published var isTicketLoading: Bool = false
@Published var ticketError: String?
// MARK: - Callbacks
var onBack: (() -> Void)?
var onLoginSuccess: (() -> Void)?
// MARK: - Private Properties
private var cancellables = Set<AnyCancellable>()
// MARK: - Enums
enum LoginStep: Equatable {
case input //
case authenticating // OAuth
case gettingTicket // Ticket
case completed //
case failed //
}
// MARK: - Computed Properties
var isLoginButtonEnabled: Bool {
return !isLoading && !userID.isEmpty && !password.isEmpty
}
// MARK: - Public Methods
func onBackTapped() {
onBack?()
}
func onLoginTapped() {
guard isLoginButtonEnabled else { return }
isLoading = true
errorMessage = nil
ticketError = nil
loginStep = .authenticating
Task {
do {
let result = try await performLogin()
await MainActor.run {
self.handleLoginResult(result)
}
} catch {
await MainActor.run {
self.handleLoginError(error)
}
}
}
}
func onRecoverPasswordTapped() {
showRecoverPassword = true
}
func onRecoverPasswordBack() {
showRecoverPassword = false
}
// MARK: - Private Methods
private func performLogin() async throws -> Bool {
// OAuth
let accountModel = try await performOAuthAuthentication()
// Ticket
let completeAccountModel = try await performTicketRequest(accountModel: accountModel)
// AccountModel
await UserInfoManager.saveAccountModel(completeAccountModel)
// API
await fetchUserInfoIfNeeded(accountModel: completeAccountModel)
return true
}
// MARK: - OAuth
private func performOAuthAuthentication() async throws -> AccountModel {
// 使LoginHelperDES
guard let loginRequest = await LoginHelper.createIDLoginRequest(
userID: userID,
password: password
) else {
throw APIError.custom("DES加密失败")
}
let apiService = LiveAPIService()
let response: IDLoginResponse = try await apiService.request(loginRequest)
if response.code == 200, let data = response.data {
// API
if let userInfo = data.userInfo {
await UserInfoManager.saveUserInfo(userInfo)
}
// ticket
guard let accountModel = AccountModel.from(loginData: data) else {
throw APIError.custom("账户信息无效")
}
return accountModel
} else {
throw APIError.custom(response.message ?? "Login failed")
}
}
// MARK: - Ticket
private func performTicketRequest(accountModel: AccountModel) async throws -> AccountModel {
await MainActor.run {
self.isTicketLoading = true
self.ticketError = nil
self.loginStep = .gettingTicket
}
let apiService = LiveAPIService()
// ticket
let ticketRequest = TicketHelper.createTicketRequest(
accessToken: accountModel.accessToken ?? "",
uid: accountModel.uid.flatMap { Int($0) }
)
let ticketResponse: TicketResponse = try await apiService.request(ticketRequest)
await MainActor.run {
self.isTicketLoading = false
}
if ticketResponse.isSuccess {
if let ticket = ticketResponse.ticket {
debugInfoSync("✅ Ticket 获取成功: \(ticket)")
// AccountModelticket
let completeAccountModel = accountModel.withTicket(ticket)
return completeAccountModel
} else {
throw APIError.custom("Ticket为空")
}
} else {
throw APIError.custom(ticketResponse.errorMessage)
}
}
// MARK: -
private func fetchUserInfoIfNeeded(accountModel: AccountModel) async {
// API
let apiService = LiveAPIService()
if let userInfo = await UserInfoManager.fetchUserInfoFromServer(
uid: accountModel.uid,
apiService: apiService
) {
await UserInfoManager.saveUserInfo(userInfo)
debugInfoSync("✅ 用户信息获取成功")
} else {
debugErrorSync("❌ 用户信息获取失败,但不影响登录流程")
}
}
private func handleLoginResult(_ success: Bool) {
isLoading = false
isTicketLoading = false
if success {
loginStep = .completed
debugInfoSync("✅ ID 登录完整流程成功")
onLoginSuccess?()
}
}
private func handleLoginError(_ error: Error) {
isLoading = false
isTicketLoading = false
errorMessage = error.localizedDescription
loginStep = .failed
debugErrorSync("❌ ID 登录失败: \(error.localizedDescription)")
}
}