初始提交
This commit is contained in:
157
suixinkan/Features/Profile/ViewModels/ProfileViewModel.swift
Normal file
157
suixinkan/Features/Profile/ViewModels/ProfileViewModel.swift
Normal file
@ -0,0 +1,157 @@
|
||||
//
|
||||
// ProfileViewModel.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/6/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 个人信息页 ViewModel,负责资料加载、编辑状态和提交更新。
|
||||
final class ProfileViewModel {
|
||||
var userInfo: UserInfoResponse?
|
||||
var realNameInfo: RealNameInfo?
|
||||
var isLoading = false
|
||||
var isSaving = false
|
||||
var isEditingProfile = false
|
||||
var editingNickname = ""
|
||||
|
||||
var displayNickname: String {
|
||||
nonEmpty(userInfo?.nickname) ?? "未设置昵称"
|
||||
}
|
||||
|
||||
var displayRealName: String {
|
||||
nonEmpty(userInfo?.realName) ?? "--"
|
||||
}
|
||||
|
||||
var displayPhone: String {
|
||||
nonEmpty(userInfo?.phone) ?? "--"
|
||||
}
|
||||
|
||||
var displayAvatarURL: String {
|
||||
nonEmpty(userInfo?.avatar) ?? ""
|
||||
}
|
||||
|
||||
var accountStatusText: String {
|
||||
nonEmpty(userInfo?.statusName) ?? "--"
|
||||
}
|
||||
|
||||
var realNameStatusText: String {
|
||||
guard let realNameInfo else { return "点击去实名认证" }
|
||||
switch realNameInfo.auditStatus {
|
||||
case 2:
|
||||
return "已实名认证"
|
||||
case 3:
|
||||
return "审核不通过"
|
||||
default:
|
||||
return "审核中"
|
||||
}
|
||||
}
|
||||
|
||||
/// 重新加载用户资料和实名认证状态。
|
||||
func reload(api: ProfileAPI) async throws {
|
||||
guard !isLoading else { return }
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
|
||||
async let user = api.userInfo()
|
||||
async let realName = api.realNameInfo()
|
||||
let result = try await (user, realName)
|
||||
|
||||
userInfo = result.0
|
||||
realNameInfo = result.1.realNameInfo
|
||||
}
|
||||
|
||||
/// 进入昵称编辑状态,并把当前昵称填入编辑框。
|
||||
func beginEditing() {
|
||||
editingNickname = displayNickname == "未设置昵称" ? "" : displayNickname
|
||||
isEditingProfile = true
|
||||
}
|
||||
|
||||
/// 退出昵称编辑状态并清空编辑输入。
|
||||
func cancelEditing() {
|
||||
editingNickname = ""
|
||||
isEditingProfile = false
|
||||
}
|
||||
|
||||
/// 保存昵称修改,并同步更新本地 userInfo。
|
||||
func saveProfile(api: ProfileAPI) async throws {
|
||||
let nextNickname = editingNickname.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !nextNickname.isEmpty else {
|
||||
throw ProfileValidationError.emptyNickname
|
||||
}
|
||||
|
||||
let nicknameChanged = nextNickname != displayNickname
|
||||
guard nicknameChanged else {
|
||||
cancelEditing()
|
||||
return
|
||||
}
|
||||
|
||||
guard !isSaving else { return }
|
||||
isSaving = true
|
||||
defer { isSaving = false }
|
||||
|
||||
try await api.updateUserInfo(nickname: nextNickname)
|
||||
var nextUser = userInfo ?? UserInfoResponse()
|
||||
nextUser = UserInfoResponse(
|
||||
avatar: nextUser.avatar,
|
||||
realName: nextUser.realName,
|
||||
phone: nextUser.phone,
|
||||
nickname: nextNickname,
|
||||
roleName: nextUser.roleName,
|
||||
status: nextUser.status,
|
||||
statusName: nextUser.statusName
|
||||
)
|
||||
userInfo = nextUser
|
||||
cancelEditing()
|
||||
}
|
||||
|
||||
/// 修改用户密码,并校验最小长度。
|
||||
func updatePassword(_ password: String, api: ProfileAPI) async throws {
|
||||
let nextPassword = password.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard nextPassword.count >= 6 else {
|
||||
throw ProfileValidationError.shortPassword
|
||||
}
|
||||
|
||||
guard !isSaving else { return }
|
||||
isSaving = true
|
||||
defer { isSaving = false }
|
||||
|
||||
try await api.updateUserInfo(password: nextPassword)
|
||||
}
|
||||
|
||||
/// 将最新 userInfo 合成为全局 AccountProfile,保留已有 userId 等兜底字段。
|
||||
func accountProfileFallback(_ fallback: AccountProfile?) -> AccountProfile? {
|
||||
guard let userInfo else { return fallback }
|
||||
return AccountProfile(
|
||||
userId: fallback?.userId ?? "",
|
||||
displayName: nonEmpty(userInfo.nickname) ?? nonEmpty(userInfo.realName) ?? fallback?.displayName ?? "未设置昵称",
|
||||
phone: nonEmpty(userInfo.phone) ?? fallback?.phone,
|
||||
avatarURL: nonEmpty(userInfo.avatar) ?? fallback?.avatarURL
|
||||
)
|
||||
}
|
||||
|
||||
/// 去除空白字符后返回非空字符串。
|
||||
private func nonEmpty(_ value: String?) -> String? {
|
||||
let text = value?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
return text.isEmpty ? nil : text
|
||||
}
|
||||
}
|
||||
|
||||
/// 个人信息页校验错误实体,表示昵称和密码输入不符合要求。
|
||||
enum ProfileValidationError: LocalizedError {
|
||||
case emptyNickname
|
||||
case shortPassword
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .emptyNickname:
|
||||
"请输入昵称"
|
||||
case .shortPassword:
|
||||
"密码至少 6 位"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user