import SwiftUI import Foundation // 搜索历史记录管理器(保持不变) class SearchHistoryManager { static let shared = SearchHistoryManager() private let userDefaultsKey = "search_history" private let maxHistoryCount = 50 var history: [String] { UserDefaults.standard.array(forKey: userDefaultsKey) as? [String] ?? [] } func addHistory(_ query: String) { var newHistory = history if let index = newHistory.firstIndex(of: query) { newHistory.remove(at: index) } newHistory.insert(query, at: 0) if newHistory.count > maxHistoryCount { newHistory = Array(newHistory.prefix(maxHistoryCount)) } UserDefaults.standard.set(newHistory, forKey: userDefaultsKey) } func removeHistory(_ query: String) { var newHistory = history if let index = newHistory.firstIndex(of: query) { newHistory.remove(at: index) UserDefaults.standard.set(newHistory, forKey: userDefaultsKey) } } func clearAllHistory() { UserDefaults.standard.removeObject(forKey: userDefaultsKey) } } // 修改后的搜索视图(重点调整历史记录条目的交互) struct SearchView: View { @State private var searchQuery: String = "" @State private var searchResults: [AlbumItem] = [] @State private var isSearching: Bool = false @State private var showAlert: Bool = false @State private var alertMessage: String = "" @State private var searchHistory: [String] = [] var body: some View { NavigationStack { Form { Section(header: Text("Jetson Media")) { HStack { TextField("输入搜索内容", text: $searchQuery) .textFieldStyle(RoundedBorderTextFieldStyle()) .onChange(of: searchQuery) { if $0.isEmpty { loadHistory() } } Button(action: performSearch) { if isSearching { ProgressView() } else { Image(systemName: "magnifyingglass") } } .disabled(searchQuery.isEmpty || isSearching) } } // 搜索历史区域(调整条目交互) if searchQuery.isEmpty && !searchHistory.isEmpty { Section(header: HStack { Text("搜索历史") Spacer() Button("清除全部") { SearchHistoryManager.shared.clearAllHistory() loadHistory() } .foregroundColor(.red) .font(.subheadline) }) { ForEach(searchHistory, id: \.self) { query in // 用HStack包裹,区分文本点击和删除按钮点击 HStack { // 文本区域:仅触发搜索 Text(query) .foregroundColor(.primary) .frame(maxWidth: .infinity, alignment: .leading) .onTapGesture { // 点击文本直接搜索 searchQuery = query performSearch() } // 删除按钮:仅触发删除 Button(action: { SearchHistoryManager.shared.removeHistory(query) loadHistory() }) { Image(systemName: "xmark.circle.fill") .foregroundColor(.gray) } .buttonStyle(PlainButtonStyle()) // 避免按钮点击影响整个行 } } } } // 搜索结果区域(保持不变) if !searchResults.isEmpty { Section(header: Text("搜索结果")) { ForEach(searchResults, id: \.album_id) { item in NavigationLink(destination: PhotoView(albumId: item.album_id)) { VStack(alignment: .leading) { Text(item.title) .font(.headline) Text("ID: \(item.album_id)") .font(.subheadline) .foregroundColor(.gray) } } } } } } .navigationTitle("搜索") .onAppear { loadHistory() } } .alert(isPresented: $showAlert) { Alert(title: Text("提示"), message: Text(alertMessage), dismissButton: .default(Text("确定"))) } } private func loadHistory() { searchHistory = SearchHistoryManager.shared.history } private func performSearch() { guard !searchQuery.isEmpty else { return } SearchHistoryManager.shared.addHistory(searchQuery) loadHistory() isSearching = true searchResults = [] let encodedQuery = searchQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? searchQuery let urlString = Config.shared.apiURL( path: "\(Config.Path.search)?search_query=\(encodedQuery)&page=1" ) guard let url = URL(string: urlString) else { showAlert(message: "无效的URL") isSearching = false return } let task = URLSession.shared.dataTask(with: url) { data, response, error in DispatchQueue.main.async { self.isSearching = false if let error = error { self.showAlert(message: "搜索失败: \(error.localizedDescription)") return } guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { self.showAlert(message: "服务器返回错误") return } guard let data = data else { self.showAlert(message: "没有接收到数据") return } do { let decoder = JSONDecoder() self.searchResults = try decoder.decode([AlbumItem].self, from: data) } catch { print("解码错误: \(error)") self.showAlert(message: "解析数据失败: \(error.localizedDescription)") } } } task.resume() } private func showAlert(message: String) { alertMessage = message showAlert = true } }