diff --git a/dimensionhub/Views/DateNavView.swift b/dimensionhub/Views/Index/DateNavView.swift similarity index 100% rename from dimensionhub/Views/DateNavView.swift rename to dimensionhub/Views/Index/DateNavView.swift diff --git a/dimensionhub/Views/Search/SearchBar.swift b/dimensionhub/Views/Search/SearchBar.swift new file mode 100644 index 0000000..65e6c22 --- /dev/null +++ b/dimensionhub/Views/Search/SearchBar.swift @@ -0,0 +1,79 @@ +// +// SearchBar.swift +// dimensionhub +// +// Created by 安礼成 on 2025/4/8. +// + +import SwiftUI + +// 搜索框 +struct SearchBar: View { + @State private var isSearching = false + + @Binding var searchText: String + var placeholder: String + var onSearch: () -> Void + + @FocusState private var isFocused + + var body: some View { + // 自定义搜索栏 + HStack(alignment: .center, spacing: 8) { + // 输入框 + TextField(placeholder, text: $searchText) + .textFieldStyle(PlainTextFieldStyle()) + .keyboardType(.default) + .focused($isFocused) + .submitLabel(.search) + .onSubmit { + onSearch() + } + .contentShape(Rectangle()) + .padding(8) + .background(Color(.systemGray6)) + .cornerRadius(8) + .overlay(alignment: .trailing) { + HStack { + if isSearching { + Button(action: { + searchText = "" + }) { + Image(systemName: "multiply.circle.fill") + .foregroundColor(.gray) + .padding(.trailing, 8) + } + } + } + } + .frame(maxWidth: .infinity) + + // 搜索按钮 + Button { + onSearch() + } label: { + Text("搜索") + .font(.system(size: 18)) + .foregroundColor(.gray) + } + .padding(.trailing, 10) + .disabled(searchText.isEmpty) + } + .onChange(of: searchText) { + if searchText.trimmingCharacters(in: .whitespaces).isEmpty { + isSearching = false + } else { + isSearching = true + } + } + .onAppear { + DispatchQueue.main.async { + isFocused = true + } + } + } +} + +//#Preview { +// SearchBar() +//} diff --git a/dimensionhub/Views/Search/SearchDramaGroupView.swift b/dimensionhub/Views/Search/SearchDramaGroupView.swift new file mode 100644 index 0000000..7fe29b1 --- /dev/null +++ b/dimensionhub/Views/Search/SearchDramaGroupView.swift @@ -0,0 +1,64 @@ +// +// SearchDramaGroupView.swift +// dimensionhub +// +// Created by 安礼成 on 2025/4/8. +// + +import SwiftUI + +// 显示分组信息 +struct SearchDramaGroupView: View { + let group: SearchModel.DramaGroup + + var body: some View { + VStack(alignment: .center, spacing: 10) { + ForEach(group.items, id: \.id) { item in + NavigationLink(destination: DetailView(id: item.id)) { + AsyncImage(url: URL(string: item.thumb)) { phase in + switch phase { + case .empty: + ProgressView() + case .success(let image): + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(height: 180) + .clipped() + default: + Image("ph_img_big") + .resizable() + .aspectRatio(contentMode: .fill) + .clipped() + } + + } + .frame(width: 370, height: 180) + .overlay(alignment: .topLeading) { + VStack(alignment: .leading, spacing: 8) { + Text(item.name) + .font(.system(size: 16)) + .foregroundColor(.white) + .lineLimit(1) + + Text(item.status) + .font(.system(size: 12)) + .foregroundColor(.white) + .lineLimit(1) + } + .padding(5) + .background( + Color.black.opacity(0.6) + ) + .cornerRadius(5) + .padding(8) + } + } + } + } + } +} + +//#Preview { +// SearchDramaGroupView() +//} diff --git a/dimensionhub/Views/Search/SearchModel.swift b/dimensionhub/Views/Search/SearchModel.swift new file mode 100644 index 0000000..995871f --- /dev/null +++ b/dimensionhub/Views/Search/SearchModel.swift @@ -0,0 +1,54 @@ +// +// SearchModel.swift +// dimensionhub +// +// Created by 安礼成 on 2025/4/8. +// + +import Foundation +import Observation + +@Observable +final class SearchModel { + + var dramaGroups: [DramaGroup] = [] + + // 请求的数据类型 + struct DramaGroup: Codable { + struct Item: Codable { + let id: Int + let name: String + let time: Int + let thumb: String + let status: String + } + + let group_id: String + let group_name: String + let items: [Item] + } + + // 搜素功能 + func search(userId: String, name: String) async { + guard let encodeName = name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { + await MainActor.run { + self.dramaGroups = [] + } + + return + } + + let response = await API.searchDrama(userId: userId, name: encodeName, as: [DramaGroup].self) + switch response { + case .result(let dramaGroups): + await MainActor.run { + self.dramaGroups = dramaGroups + } + case .error(let int32, let string): + print("error_code: \(int32), message: \(string)") + await MainActor.run { + self.dramaGroups = [] + } + } + } +} diff --git a/dimensionhub/Views/Search/SearchView.swift b/dimensionhub/Views/Search/SearchView.swift new file mode 100644 index 0000000..5cc8ed0 --- /dev/null +++ b/dimensionhub/Views/Search/SearchView.swift @@ -0,0 +1,133 @@ +// +// SearchView.swift +// dimensionhub +// +// Created by 安礼成 on 2025/4/1. +// + +import SwiftUI +import SwiftData + +struct SearchView: View { + @Environment(\.modelContext) var modelContext + @State var showHistoryNum: Int = 2 + + @AppStorage("userId") private var userId: String = Utils.defaultUserId() + @Environment(\.dismiss) var dismiss + @State var searchText: String = "" + + @State var searchModel = SearchModel() + + var body: some View { + VStack { + HStack(alignment: .center, spacing: 0) { + Button(action: { dismiss() }) { + Image(systemName: "chevron.left") + .font(.system(size: 20, weight: .medium)) + .foregroundColor(.black) + } + + Spacer() + + SearchBar(searchText: $searchText, placeholder: "搜索...") { + let trimmedSearchText = searchText.trimmingCharacters(in: .whitespacesAndNewlines) + if !trimmedSearchText.isEmpty { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + Task.detached { + await searchModel.search(userId: userId, name: trimmedSearchText) + } + +// let historyModel = SearchHistory(keyword: trimmedSearchText, timestamp: Date()) +// modelContext.insert(historyModel) + } + } + } + .frame(height: 50) + .padding([.top, .bottom], 8) + + ScrollView(.vertical, showsIndicators: false) { + // 基于日期的更新列表 + LazyVStack(alignment: .center, spacing: 10) { + ForEach(searchModel.dramaGroups, id: \.group_id) { group in + SearchDramaGroupView(group: group) + } + } + } + + Spacer() + } + .navigationTitle("") + .navigationBarBackButtonHidden(true) + .padding(8) + .ignoresSafeArea(edges: .bottom) + } + +} + +extension SearchView { + + struct SearchHistoryView1: View { + @Query(sort: \SearchHistory.timestamp, order: .reverse) private var historyItems: [SearchHistory] + + var body: some View { + VStack { + if historyItems.count > 5 { + ForEach(Array(0..<5), id: \.self) { idx in + SearchHistoryItemView(history: historyItems[idx]) { + print("click me") + } + } + + Button { + //self.showHistoryNum = 5 + } label: { + Text("查看更多历史") + .font(.system(size: 16)) + .foregroundColor(.gray) + } + .buttonStyle(.plain) + } else { + ForEach(historyItems) { history in + SearchHistoryItemView(history: history) { + print("click me") + } + } + } + } + } + } + + // 搜索历史 + struct SearchHistoryItemView: View { + let history: SearchHistory + var onClick: () -> Void + + var body: some View { + HStack(alignment: .center, spacing: 20) { + Image("lost_network") + .resizable() + .clipShape(Circle()) + .clipped() + .frame(width: 25, height: 25) + + Text(history.keyword) + .font(.system(size: 16)) + .lineLimit(1) + .frame(width: 280, alignment: .leading) + + Button(action: { + onClick() + }) { + Image(systemName: "xmark") + .font(.system(size: 14)) + .foregroundColor(.gray) + } + } + } + } + +} + +#Preview { + SearchView() +} diff --git a/dimensionhub/Views/SearchView.swift b/dimensionhub/Views/SearchView.swift deleted file mode 100644 index 914240e..0000000 --- a/dimensionhub/Views/SearchView.swift +++ /dev/null @@ -1,286 +0,0 @@ -// -// SearchView.swift -// dimensionhub -// -// Created by 安礼成 on 2025/4/1. -// - -import SwiftUI -import SwiftData - -struct SearchView: View { - @Environment(\.modelContext) var modelContext - @State var showHistoryNum: Int = 2 - - @AppStorage("userId") private var userId: String = Utils.defaultUserId() - @Environment(\.dismiss) var dismiss - @State var searchText: String = "" - - // 请求的数据类型 - struct DramaGroup: Codable { - struct Item: Codable { - let id: Int - let name: String - let time: Int - let thumb: String - let status: String - } - - let group_id: String - let group_name: String - let items: [Item] - } - - @State var dramaGroups: [DramaGroup] = [] - - var body: some View { - VStack { - HStack(alignment: .center, spacing: 0) { - Button(action: { dismiss() }) { - Image(systemName: "chevron.left") - .font(.system(size: 20, weight: .medium)) - .foregroundColor(.black) - } - - Spacer() - - SearchBar(searchText: $searchText, placeholder: "搜索...") { - let trimmedSearchText = searchText.trimmingCharacters(in: .whitespacesAndNewlines) - if !trimmedSearchText.isEmpty { - UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) - Task { - let dramaGroups = await self.search(userId: userId, name: trimmedSearchText) - DispatchQueue.main.async { - self.dramaGroups = dramaGroups - } - } - -// let historyModel = SearchHistory(keyword: trimmedSearchText, timestamp: Date()) -// modelContext.insert(historyModel) - } - } - } - .frame(height: 50) - .padding([.top, .bottom], 8) - - ScrollView(.vertical, showsIndicators: false) { - // 基于日期的更新列表 - LazyVStack(alignment: .center, spacing: 10) { - ForEach(dramaGroups, id: \.group_id) { group in - DramaGroupView(group: group) - } - } - } - - Spacer() - } - .navigationTitle("") - .navigationBarBackButtonHidden(true) - .padding(8) - .ignoresSafeArea(edges: .bottom) - } - - // 搜素功能 - func search(userId: String, name: String) async -> [DramaGroup] { - guard let encodeName = name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { - return [] - } - - let response = await API.searchDrama(userId: userId, name: encodeName, as: [DramaGroup].self) - switch response { - case .result(let dramaGroups): - return dramaGroups - case .error(let int32, let string): - print("error_code: \(int32), message: \(string)") - return [] - } - } -} - -extension SearchView { - - // 显示分组信息 - struct DramaGroupView: View { - let group: DramaGroup - - var body: some View { - VStack(alignment: .center, spacing: 10) { - ForEach(group.items, id: \.id) { item in - NavigationLink(destination: DetailView(id: item.id)) { - AsyncImage(url: URL(string: item.thumb)) { phase in - switch phase { - case .empty: - ProgressView() - case .success(let image): - image - .resizable() - .aspectRatio(contentMode: .fill) - .frame(height: 180) - .clipped() - default: - Image("ph_img_big") - .resizable() - .aspectRatio(contentMode: .fill) - .clipped() - } - - } - .frame(width: 370, height: 180) - .overlay(alignment: .topLeading) { - VStack(alignment: .leading, spacing: 8) { - Text(item.name) - .font(.system(size: 16)) - .foregroundColor(.white) - .lineLimit(1) - - Text(item.status) - .font(.system(size: 12)) - .foregroundColor(.white) - .lineLimit(1) - } - .padding(5) - .background( - Color.black.opacity(0.6) - ) - .cornerRadius(5) - .padding(8) - } - } - } - } - } - } - - // 搜索框 - struct SearchBar: View { - @State private var isSearching = false - - @Binding var searchText: String - var placeholder: String - var onSearch: () -> Void - - @FocusState private var isFocused - - var body: some View { - // 自定义搜索栏 - HStack(alignment: .center, spacing: 8) { - // 输入框 - TextField(placeholder, text: $searchText) - .textFieldStyle(PlainTextFieldStyle()) - .keyboardType(.default) - .focused($isFocused) - .submitLabel(.search) - .onSubmit { - onSearch() - } - .contentShape(Rectangle()) - .padding(8) - .background(Color(.systemGray6)) - .cornerRadius(8) - .overlay(alignment: .trailing) { - HStack { - if isSearching { - Button(action: { - searchText = "" - }) { - Image(systemName: "multiply.circle.fill") - .foregroundColor(.gray) - .padding(.trailing, 8) - } - } - } - } - .frame(maxWidth: .infinity) - - // 搜索按钮 - Button { - onSearch() - } label: { - Text("搜索") - .font(.system(size: 18)) - .foregroundColor(.gray) - } - .padding(.trailing, 10) - .disabled(searchText.isEmpty) - } - .onChange(of: searchText) { - if searchText.trimmingCharacters(in: .whitespaces).isEmpty { - isSearching = false - } else { - isSearching = true - } - } - .onAppear { - DispatchQueue.main.async { - isFocused = true - } - } - } - } - - - struct SearchHistoryView1: View { - @Query(sort: \SearchHistory.timestamp, order: .reverse) private var historyItems: [SearchHistory] - - var body: some View { - VStack { - if historyItems.count > 5 { - ForEach(Array(0..<5), id: \.self) { idx in - SearchHistoryItemView(history: historyItems[idx]) { - print("click me") - } - } - - Button { - //self.showHistoryNum = 5 - } label: { - Text("查看更多历史") - .font(.system(size: 16)) - .foregroundColor(.gray) - } - .buttonStyle(.plain) - } else { - ForEach(historyItems) { history in - SearchHistoryItemView(history: history) { - print("click me") - } - } - } - } - } - } - - // 搜索历史 - struct SearchHistoryItemView: View { - let history: SearchHistory - var onClick: () -> Void - - var body: some View { - HStack(alignment: .center, spacing: 20) { - Image("lost_network") - .resizable() - .clipShape(Circle()) - .clipped() - .frame(width: 25, height: 25) - - Text(history.keyword) - .font(.system(size: 16)) - .lineLimit(1) - .frame(width: 280, alignment: .leading) - - Button(action: { - onClick() - }) { - Image(systemName: "xmark") - .font(.system(size: 14)) - .foregroundColor(.gray) - } - } - } - } - -} - -#Preview { - SearchView() -}