PetListViewController.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. //
  2. // PetListViewController.swift
  3. // VenusKitto
  4. //
  5. // Created by Neoa on 2025/8/25.
  6. //
  7. import Foundation
  8. import UIKit
  9. struct Pet: Codable {
  10. let id: String
  11. let name: String
  12. let avatar: String
  13. let breedName: String
  14. let age: String
  15. }
  16. struct PetListResponse: Codable {
  17. let code: String
  18. let msg: String?
  19. let data: [Pet]
  20. }
  21. class PetListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
  22. private var pets: [Pet] = []
  23. var onPetSelected: ((Pet) -> Void)?
  24. func reloadPetList() {
  25. tableView.reloadData()
  26. }
  27. private let tableView = UITableView()
  28. private var selectedPet: Pet?
  29. override func viewDidLoad() {
  30. super.viewDidLoad()
  31. // Set up table view
  32. tableView.delegate = self
  33. tableView.dataSource = self
  34. tableView.register(PetTableViewCell.self, forCellReuseIdentifier: "PetCell")
  35. view.addSubview(tableView)
  36. tableView.translatesAutoresizingMaskIntoConstraints = false
  37. NSLayoutConstraint.activate([
  38. tableView.topAnchor.constraint(equalTo: view.topAnchor),
  39. tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
  40. tableView.rightAnchor.constraint(equalTo: view.rightAnchor),
  41. tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
  42. ])
  43. fetchPetList() // Request pet list
  44. }
  45. // Table view data source
  46. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  47. return pets.count
  48. }
  49. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  50. let pet = pets[indexPath.row]
  51. let cell = tableView.dequeueReusableCell(withIdentifier: "PetCell", for: indexPath) as! PetTableViewCell
  52. cell.configure(with: pet)
  53. return cell
  54. }
  55. // Table view delegate
  56. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  57. let selectedPet = pets[indexPath.row]
  58. onPetSelected?(selectedPet)
  59. self.dismiss(animated: true)
  60. // Handle switching pet or showing more details
  61. }
  62. func fetchPetList() {
  63. // Get userId from UserDefaults
  64. guard let userId = UserDefaults.standard.string(forKey: "userId"), !userId.isEmpty else {
  65. print("⚠️ fetchPetList: userId missing or empty in UserDefaults")
  66. return
  67. }
  68. // Build URL with query parameter
  69. var components = URLComponents(string: "\(baseURL)/petRecordPet/queryPetList")
  70. components?.queryItems = [URLQueryItem(name: "userId", value: userId)]
  71. guard let url = components?.url else {
  72. print("⚠️ fetchPetList: Failed to build URL with userId param")
  73. return
  74. }
  75. print("🐾 fetchPetList URL: \(url.absoluteString)")
  76. var request = URLRequest(url: url)
  77. request.httpMethod = "GET"
  78. request.setValue("application/json", forHTTPHeaderField: "Content-Type")
  79. if let token = UserDefaults.standard.string(forKey: "userToken") {
  80. request.setValue(token, forHTTPHeaderField: "Authorization")
  81. }
  82. let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
  83. if let httpResp = response as? HTTPURLResponse {
  84. print("🐾 fetchPetList HTTP status: \(httpResp.statusCode)")
  85. }
  86. if let error = error {
  87. print("Error: \(error)")
  88. return
  89. }
  90. guard let data = data else { return }
  91. do {
  92. let decoder = JSONDecoder()
  93. let responseObject = try decoder.decode(PetListResponse.self, from: data)
  94. if responseObject.code == "200" {
  95. self?.pets = responseObject.data
  96. DispatchQueue.main.async {
  97. self?.reloadPetList()
  98. }
  99. } else {
  100. print("Error: \(responseObject.msg ?? "Unknown error")")
  101. }
  102. } catch {
  103. print("Error decoding response: \(error)")
  104. }
  105. }
  106. task.resume()
  107. }
  108. }
  109. class PetTableViewCell: UITableViewCell {
  110. private let avatarImageView = UIImageView()
  111. private let nameLabel = UILabel()
  112. override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
  113. super.init(style: style, reuseIdentifier: reuseIdentifier)
  114. avatarImageView.contentMode = .scaleAspectFill
  115. avatarImageView.layer.cornerRadius = 24
  116. avatarImageView.clipsToBounds = true
  117. contentView.addSubview(avatarImageView)
  118. nameLabel.font = .systemFont(ofSize: 16)
  119. contentView.addSubview(nameLabel)
  120. avatarImageView.translatesAutoresizingMaskIntoConstraints = false
  121. nameLabel.translatesAutoresizingMaskIntoConstraints = false
  122. NSLayoutConstraint.activate([
  123. avatarImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 16),
  124. avatarImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
  125. avatarImageView.widthAnchor.constraint(equalToConstant: 48),
  126. avatarImageView.heightAnchor.constraint(equalToConstant: 48),
  127. nameLabel.leftAnchor.constraint(equalTo: avatarImageView.rightAnchor, constant: 16),
  128. nameLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
  129. ])
  130. }
  131. required init?(coder: NSCoder) {
  132. fatalError("init(coder:) has not been implemented")
  133. }
  134. func configure(with pet: Pet) {
  135. nameLabel.text = pet.name
  136. if let url = URL(string: pet.avatar) {
  137. loadImage(from: url)
  138. }
  139. }
  140. private func loadImage(from url: URL) {
  141. // Create a data task to load the image
  142. URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
  143. // Check for errors
  144. if let error = error {
  145. print("Error loading image: \(error)")
  146. return
  147. }
  148. // Check if data is valid
  149. guard let data = data else {
  150. print("No data returned")
  151. return
  152. }
  153. // Update the image on the main thread
  154. DispatchQueue.main.async {
  155. if let image = UIImage(data: data) {
  156. self?.avatarImageView.image = image
  157. }
  158. }
  159. }.resume() // Start the download
  160. }
  161. }