// 我的模块 // import SwiftUI import Combine import Alamofire import Kingfisher // 数据模型 struct User: Decodable { let user_id: Int let nickname: String let avatar_url: String let signature: String let territory: String } // 视图模型 class UserProfileViewModel: ObservableObject { static let shared = UserProfileViewModel() @Published var user: User? @Published var isLoading = true @Published var errorMessage: String? private init() { fetchUserProfile() } private var cancellable: AnyCancellable? func fetchUserProfile() { makeAlamofireRequest(api: apiprefixV1 + userInfoApi, method: .post, param: Optional.none) { (result: Result, AFError>) in DispatchQueue.main.async { self.isLoading = false switch result { case .success(let body): if body.code == 0 { self.user = body.data } else { self.errorMessage = body.message } case .failure(let error): self.errorMessage = error.localizedDescription } } } } } struct MineView: View { @ObservedObject private var viewModel = UserProfileViewModel.shared @State var tagActive = 0 @State private var showLoginView = false @State var items: [TargetData1] = [ // TargetData1(title:"训练完喝瓶AD钙",progress: 14.29, status_str:"未完成今日打卡",check: true, image:"image-5"), // TargetData1(title:"训练完喝瓶AD钙",progress: 14.29, status_str:"未完成今日打卡", check:true, image:"image-5"), // TargetData1(title:"哭泣的骆驼",progress: 14.29,status_str: "未完成今日打卡",check: true, image:"image-5"), // TargetData1(title:"训练完喝瓶AD钙",progress: 14.29, status_str:"未完成今日打卡",check: true, image:"image-5") ] var body: some View { NavigationStack{ VStack { // 头部信息 VStack { // 标题 HStack { Button {} label: { Image(systemName: "lines.measurement.horizontal") } Spacer() Button (action: { // 退出登录 UserDefaults.standard.removeObject(forKey: "userToken") // 展示登录界面 viewModel.user = nil showLoginView = true }) { Image(systemName: "square.and.arrow.up") .rotationEffect(.degrees(90)) } } .padding(.horizontal, 12) .foregroundStyle(.white) .font(.system(size: 21)) // 用户信息 VStack(alignment: .leading) { if let user = viewModel.user { HStack(spacing: 20) { KFImage(URL(string: user.avatar_url)) .resizable() .placeholder { ProgressView() } .aspectRatio(contentMode: .fill) .frame(width: 100, height: 100) .clipShape(Circle()) .overlay(Circle().stroke(Color.white, lineWidth: 2)) VStack(alignment: .leading) { Text(user.nickname) .SetTextStyle(size: 21, color: .white) .bold() HStack { Text("小红书号:1212121") .SetTextStyle(size: 12, color: .white) Image(systemName: "qrcode") .foregroundStyle(.white) .font(.system(size: 12)) } .padding(.top, 1) HStack { Text("IP属地:\(user.territory)") .SetTextStyle(size: 12, color: .white) Image(systemName: "globe.asia.australia") .foregroundStyle(.white) .font(.system(size: 12)) } .padding(.top, 1) } Spacer() } Text(user.signature) .SetTextStyle(size: 17, color: .white) .padding(.top, 12) // 信息标签 HStack { Button {} label: { Text("处女座") .font(.system(size: 12)) .padding(.vertical, 2) .padding(.horizontal, 7) .background(.white.opacity(0.2), in: RoundedRectangle(cornerRadius: 12)) } Button {} label: { Text("四川成都") .font(.system(size: 12)) .padding(.vertical, 2) .padding(.horizontal, 7) .background(.white.opacity(0.2), in: RoundedRectangle(cornerRadius: 12)) } Button {} label: { Text("外卖员") .font(.system(size: 12)) .padding(.vertical, 2) .padding(.horizontal, 7) .background(.white.opacity(0.2), in: RoundedRectangle(cornerRadius: 12)) } } .foregroundStyle(.white) } else { // 未登录状态下显示登录按钮 VStack { Button(action: { showLoginView = true }) { VStack { Image(systemName: "person.crop.circle.fill") .resizable() .frame(width: 100, height: 100) .foregroundColor(.white) Text("登录") .SetTextStyle(size: 21, color: .white) .bold() } } } } // 操作栏 HStack { VStack { Text("12") .font(.system(size: 13)) Text("关注") .font(.system(size: 11)) } VStack { Text("4") .font(.system(size: 13)) Text("粉丝") .font(.system(size: 11)) } VStack { Text("32") .font(.system(size: 13)) Text("获赞与收藏") .font(.system(size: 11)) } Spacer() Button { } label: { Text("编辑资料") .font(.system(size: 14)) .padding(.vertical, 2) .padding(.horizontal, 7) .background(.white.opacity(0.2), in: RoundedRectangle(cornerRadius: 12)) } Button {} label: { Image(systemName: "ellipsis.circle") .font(.system(size: 14)) .padding(.vertical, 2) .padding(.horizontal, 7) .background(.white.opacity(0.2), in: RoundedRectangle(cornerRadius: 12)) } } .foregroundStyle(.white) .padding(.top, 12) } .padding(.horizontal, 12) .padding(.top, 22) .padding(.bottom, 8) } .padding(.bottom, 20) .background( Image("image-3") .resizable() .aspectRatio(contentMode: .fill) .edgesIgnoringSafeArea(.top) // 确保图片覆盖到顶部安全区域 ) HStack(spacing: 35) { Button { tagActive = 0 let req = TargetMeReq(page: 1, page_size: 100, status: tagActive) getTarget(req: req) } label: { Text("全部") .foregroundStyle(tagActive == 0 ? .blue : .black) } Button { tagActive = 2 let req = TargetMeReq(page: 1, page_size: 100, status: tagActive) getTarget(req: req) } label: { Text("进行中") .foregroundStyle(tagActive == 2 ? .blue : .black) } Button { tagActive = 3 let req = TargetMeReq(page: 1, page_size: 100, status: tagActive) getTarget(req: req) } label: { Text("已完成") .foregroundStyle(tagActive == 3 ? .blue : .black) } Button { tagActive = 4 let req = TargetMeReq(page: 1, page_size: 100, status: tagActive) getTarget(req: req) } label: { Text("已逾期") .foregroundStyle(tagActive == 4 ? .blue : .black) } } .foregroundStyle(.black.opacity(0.6)) .padding(.vertical, 9) // 内容部分 List(items){item in TargetItemSwift(target: item) } } .navigationDestination(isPresented: $showLoginView) { LoginView() // 显示登录界面 } .navigationViewStyle(StackNavigationViewStyle()) // 确保在 iPad 上正确显示 .toolbar(.hidden) } .onAppear { viewModel.fetchUserProfile() let req = TargetMeReq(page: 1, page_size: 100, status: tagActive) getTarget(req: req) } } func getTarget(req: TargetMeReq){ GetMeTargets(req: req){ (result: Result, AFError>) in switch result { case .success(let body): items.removeAll() if body.code == 0 { body.data.list.forEach { T in items.append(TargetData1(title: T.name, progress: T.progress, status_str: T.status_str, check: true, image: T.image)) } } else { print(body) } case .failure(let error): debugPrint("Error: \(error)") } } } } #Preview { MineView() }