import SwiftUI /// Account details opened from Settings. struct ProfileView: View { @ObservedObject var authManager: AuthManager = .shared @State private var showAuthModal = false @State private var showLogoutConfirmation = false @State private var usernameInput = "" @State private var emailInput = "" @State private var phoneInput = "" @State private var profileMessage: String? @State private var profileMessageIsError = false var body: some View { ZStack { Color.spaceBlack.ignoresSafeArea() ScrollView { VStack(spacing: 20) { // 1. Account Profile Header accountHeaderCard .padding(.top, 12) // 2. Inline profile editing (if logged in) if authManager.isLoggedIn { profileEditorCard } // Footer footer .padding(.top, 12) .padding(.bottom, 40) } .padding(.horizontal, 16) } } .navigationTitle("账号") .navigationBarTitleDisplayMode(.inline) .sheet(isPresented: $showAuthModal) { AuthModalView(authManager: authManager) } .onAppear(perform: loadProfileInputs) .confirmationDialog("确定要退出登录吗?", isPresented: $showLogoutConfirmation, titleVisibility: .visible) { Button("退出登录", role: .destructive) { authManager.logout() } Button("取消", role: .cancel) {} } } // MARK: - 1. Account Header private var accountHeaderCard: some View { VStack(spacing: 16) { if let user = authManager.currentUser { // Authenticated Profile State HStack(spacing: 16) { ZStack { Circle() .fill(Color.primary.opacity(0.12)) .frame(width: 60, height: 60) Image(systemName: "person.crop.circle.fill") .font(.system(size: 34)) .foregroundStyle(Color.primary) } VStack(alignment: .leading, spacing: 6) { Text(user.username) .font(.system(size: 18, weight: .bold)) .foregroundStyle(Color.primary) Text(user.email ?? user.phoneNumber ?? ("用户 ID: " + String(user.id.prefix(12)))) .font(.system(size: 12)) .foregroundStyle(Color.secondary) } Spacer() Button { showLogoutConfirmation = true } label: { Text("退出") .font(.system(size: 12, weight: .regular)) .foregroundStyle(Color.recordingRed) .padding(.horizontal, 10) .padding(.vertical, 5) .overlay( RoundedRectangle(cornerRadius: 6) .stroke(Color.recordingRed.opacity(0.4), lineWidth: 1) ) } } } else { // Guest / Unauthenticated State VStack(spacing: 14) { HStack(spacing: 14) { ZStack { Circle() .fill(Color.primary.opacity(0.06)) .frame(width: 52, height: 52) Image(systemName: "person.crop.circle.badge.plus") .font(.system(size: 24)) .foregroundStyle(Color.secondary) } VStack(alignment: .leading, spacing: 4) { Text("未登录账号") .font(.system(size: 16, weight: .semibold)) .foregroundStyle(Color.primary) Text("登录/注册后支持全功能体验与 BLE 硬件绑定") .font(.system(size: 12)) .foregroundStyle(Color.secondary) } Spacer() } Button { showAuthModal = true } label: { Text("注册 / 登录账号") .font(.system(size: 14, weight: .semibold)) .foregroundStyle(Color.spaceBlack) .frame(maxWidth: .infinity) .padding(.vertical, 11) .background(Color.primary) .cornerRadius(8) } } } } .padding(16) .background(Color.cardBackground.opacity(0.25)) .businessBorder(cornerRadius: 12) } // MARK: - 2. Inline Profile Editing private var profileEditorCard: some View { VStack(alignment: .leading, spacing: 14) { Text("基本资料") .font(.system(size: 15, weight: .semibold)) .foregroundStyle(Color.primary) profileField(label: "账号名", text: $usernameInput, icon: "person") profileField(label: "电子邮箱", text: $emailInput, icon: "envelope", keyboard: .emailAddress) profileField(label: "手机号码", text: $phoneInput, icon: "phone", keyboard: .phonePad) if let profileMessage { Text(profileMessage) .font(.system(size: 12)) .foregroundStyle(profileMessageIsError ? Color.recordingRed : Color.green) } Button { saveProfile() } label: { Group { if authManager.isProcessing { ProgressView().tint(Color.spaceBlack) } else { Text("保存基本资料") .font(.system(size: 13, weight: .medium)) } } .foregroundStyle(Color.spaceBlack) .frame(maxWidth: .infinity) .padding(.vertical, 10) .background(Color.primary) .cornerRadius(8) } .disabled(authManager.isProcessing) NavigationLink { ChangePasswordModalView(authManager: authManager) } label: { HStack { Image(systemName: "key") Text("修改安全密码") Spacer() Image(systemName: "chevron.right") .font(.system(size: 11, weight: .semibold)) .foregroundStyle(Color.secondary) } .font(.system(size: 13, weight: .medium)) .foregroundStyle(Color.primary) .padding(.vertical, 10) } } .padding(16) .background(Color.cardBackground.opacity(0.25)) .businessBorder(cornerRadius: 12) } // MARK: - Actions private func profileField( label: String, text: Binding, icon: String, keyboard: UIKeyboardType = .default ) -> some View { VStack(alignment: .leading, spacing: 6) { Text(label) .font(.system(size: 12)) .foregroundStyle(Color.secondary) HStack(spacing: 10) { Image(systemName: icon) .foregroundStyle(Color.secondary) .frame(width: 18) TextField(label, text: text) .textInputAutocapitalization(.never) .autocorrectionDisabled() .keyboardType(keyboard) } .font(.system(size: 14)) .padding(.horizontal, 12) .padding(.vertical, 11) .background(Color.cardBackground.opacity(0.3)) .businessBorder(cornerRadius: 8) } } private func loadProfileInputs() { guard let user = authManager.currentUser else { return } usernameInput = user.username emailInput = user.email ?? "" phoneInput = user.phoneNumber ?? "" } private func saveProfile() { profileMessage = nil authManager.updateProfile( username: usernameInput, email: emailInput, phoneNumber: phoneInput, avatarURL: nil ) { success, error in profileMessageIsError = !success profileMessage = success ? "基本资料已保存" : (error ?? "保存失败") if success { loadProfileInputs() } } } // MARK: - Footer private var footer: some View { VStack(spacing: 4) { Text("CELESTIA TRACE USER & DEVICE") .font(.system(size: 10, weight: .bold, design: .monospaced)) .foregroundStyle(Color.secondary.opacity(0.4)) .tracking(2) Text("安全本地注册与极简日志空间") .font(.system(size: 9)) .foregroundStyle(Color.secondary.opacity(0.3)) } } } #Preview { NavigationStack { ProfileView() } }