Files
JetsonMediaIOS/Jetson Media/ui/SearchView.swift
2025-08-18 23:59:43 +08:00

210 lines
8.0 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}
}
// ID
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] = []
//
@State private var navigationPath = NavigationPath()
var body: some View {
// 使NavigationStack
NavigationStack(path: $navigationPath) {
Form {
Section(header: Text("Jetson Media")) {
HStack {
TextField("输入搜索内容或ID", 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 {
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()
}
//
.navigationDestination(for: Int.self) { albumId in
PhotoView(albumId: String(albumId))
}
}
.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 }
//
if let albumId = Int(searchQuery) {
// ID
SearchHistoryManager.shared.addHistory(searchQuery)
loadHistory()
navigationPath.append(albumId)
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
}
}