import SwiftUI import CoreData struct HistoryView: View { @StateObject private var coreDataManager = CoreDataManager.shared @State private var searchText = "" @State private var selectedFilter: HistoryFilter = .all @State private var showingCreateSheet = false @State private var showingClearAlert = false enum HistoryFilter: String, CaseIterable { case all = "all" case barcode = "barcode" case qrcode = "qrcode" case scanned = "scanned" case created = "created" case favorites = "favorites" var displayName: String { switch self { case .all: return "全部" case .barcode: return "条形码" case .qrcode: return "二维码" case .scanned: return "扫描获得" case .created: return "手动创建" case .favorites: return "收藏" } } var icon: String { switch self { case .all: return "list.bullet" case .barcode: return "barcode" case .qrcode: return "qrcode" case .scanned: return "camera.viewfinder" case .created: return "plus.circle" case .favorites: return "heart.fill" } } } var filteredItems: [HistoryItem] { let allItems = coreDataManager.fetchHistoryItems() let searchResults = allItems.filter { item in if !searchText.isEmpty { let content = item.content ?? "" let barcodeType = item.barcodeType ?? "" let qrCodeType = item.qrCodeType ?? "" return content.localizedCaseInsensitiveContains(searchText) || barcodeType.localizedCaseInsensitiveContains(searchText) || qrCodeType.localizedCaseInsensitiveContains(searchText) } return true } switch selectedFilter { case .all: return searchResults case .barcode: return searchResults.filter { $0.dataType == DataType.barcode.rawValue } case .qrcode: return searchResults.filter { $0.dataType == DataType.qrcode.rawValue } case .scanned: return searchResults.filter { $0.dataSource == DataSource.scanned.rawValue } case .created: return searchResults.filter { $0.dataSource == DataSource.created.rawValue } case .favorites: return searchResults.filter { $0.isFavorite } } } var body: some View { NavigationView { VStack(spacing: 0) { // 搜索栏 searchBar // 过滤器 filterBar // 内容列表 if filteredItems.isEmpty { emptyStateView } else { historyList } } .navigationTitle("历史记录") .navigationBarTitleDisplayMode(.large) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button(action: { showingClearAlert = true }) { Image(systemName: "trash") .foregroundColor(.red) } .disabled(coreDataManager.fetchHistoryItems().isEmpty) } ToolbarItem(placement: .navigationBarTrailing) { Button(action: { showingCreateSheet = true }) { Image(systemName: "plus") } } } .sheet(isPresented: $showingCreateSheet) { CreateCodeView() } .overlay( // 二维码详情页面导航按钮 VStack { Spacer() HStack { Spacer() if let firstQRCode = filteredItems.first(where: { $0.dataType == DataType.qrcode.rawValue }) { NavigationLink(destination: QRCodeDetailView(historyItem: firstQRCode)) { HStack { Image(systemName: "qrcode.viewfinder") Text("查看二维码详情") } .font(.title3) .foregroundColor(.white) .padding() .background(Color.orange) .cornerRadius(10) .shadow(radius: 4) } } Spacer() } .padding(.bottom, 20) } ) .alert("清空历史记录", isPresented: $showingClearAlert) { Button("取消", role: .cancel) { } Button("清空", role: .destructive) { clearHistory() } } message: { Text("确定要清空所有历史记录吗?此操作不可撤销。") } } } // MARK: - 清空历史记录 private func clearHistory() { coreDataManager.clearAllHistory() } // MARK: - 切换收藏状态 private func toggleFavorite(_ item: HistoryItem) { item.isFavorite.toggle() coreDataManager.save() } // MARK: - 删除历史记录 private func deleteHistoryItem(_ item: HistoryItem) { coreDataManager.deleteHistoryItem(item) } // MARK: - 搜索栏 private var searchBar: some View { HStack { Image(systemName: "magnifyingglass") .foregroundColor(.gray) TextField("搜索历史记录...", text: $searchText) .textFieldStyle(RoundedBorderTextFieldStyle()) } .padding(.horizontal) .padding(.vertical, 8) .background(Color(.systemBackground)) } // MARK: - 过滤器栏 private var filterBar: some View { ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 12) { ForEach(HistoryFilter.allCases, id: \.self) { filter in FilterChip( filter: filter, isSelected: selectedFilter == filter, action: { selectedFilter = filter } ) } } .padding(.horizontal) } .padding(.vertical, 8) .background(Color(.systemBackground)) } // MARK: - 历史记录列表 private var historyList: some View { List { ForEach(filteredItems) { item in HistoryItemRow( item: item, onToggleFavorite: { toggleFavorite(item) }, onDelete: { deleteHistoryItem(item) } ) } } .listStyle(PlainListStyle()) } // MARK: - 空状态视图 private var emptyStateView: some View { VStack(spacing: 20) { Image(systemName: "clock.arrow.circlepath") .font(.system(size: 60)) .foregroundColor(.gray) Text("暂无历史记录") .font(.title2) .fontWeight(.medium) .foregroundColor(.gray) Text("扫描二维码或手动创建来开始记录") .font(.body) .foregroundColor(.gray) .multilineTextAlignment(.center) Button(action: { showingCreateSheet = true }) { HStack { Image(systemName: "plus.circle.fill") Text("创建第一个记录") } .font(.headline) .foregroundColor(.white) .padding() .background(Color.blue) .cornerRadius(10) } } .padding() .frame(maxWidth: .infinity, maxHeight: .infinity) } } #Preview { HistoryView() } // MARK: - 过滤器芯片 struct FilterChip: View { let filter: HistoryView.HistoryFilter let isSelected: Bool let action: () -> Void var body: some View { Button(action: action) { HStack(spacing: 6) { Image(systemName: filter.icon) .font(.system(size: 14)) Text(filter.displayName) .font(.system(size: 14, weight: .medium)) } .padding(.horizontal, 12) .padding(.vertical, 8) .background(isSelected ? Color.blue : Color(.systemGray5)) .foregroundColor(isSelected ? .white : .primary) .cornerRadius(20) } .buttonStyle(PlainButtonStyle()) } } // MARK: - 历史记录项行 struct HistoryItemRow: View { let item: HistoryItem let onToggleFavorite: () -> Void let onDelete: () -> Void @State private var showingDetail = false var body: some View { HStack(spacing: 12) { // 类型图标 VStack { if let dataTypeString = item.dataType, let dataType = DataType(rawValue: dataTypeString) { Image(systemName: dataType.icon) .font(.system(size: 24)) .foregroundColor(.blue) } if let dataSourceString = item.dataSource, let dataSource = DataSource(rawValue: dataSourceString) { Image(systemName: dataSource.icon) .font(.system(size: 12)) .foregroundColor(.gray) } } .frame(width: 40) // 内容信息 VStack(alignment: .leading, spacing: 4) { HStack { Text(item.content ?? "") .font(.headline) .lineLimit(2) Spacer() Button(action: onToggleFavorite) { Image(systemName: item.isFavorite ? "heart.fill" : "heart") .foregroundColor(item.isFavorite ? .red : .gray) } .buttonStyle(PlainButtonStyle()) } HStack { // 类型标签 if let dataTypeString = item.dataType, let dataType = DataType(rawValue: dataTypeString) { HStack(spacing: 4) { Image(systemName: dataType.icon) .font(.system(size: 12)) Text(dataType.displayName) .font(.caption) } .padding(.horizontal, 8) .padding(.vertical, 2) .background(Color.blue.opacity(0.1)) .foregroundColor(.blue) .cornerRadius(8) } // 具体类型标签 if let barcodeTypeString = item.barcodeType, let barcodeType = BarcodeType(rawValue: barcodeTypeString) { HStack(spacing: 4) { Image(systemName: barcodeType.icon) .font(.system(size: 12)) Text(barcodeType.displayName) .font(.caption) } .padding(.horizontal, 8) .padding(.vertical, 2) .background(Color.green.opacity(0.1)) .foregroundColor(.green) .cornerRadius(8) } if let qrCodeTypeString = item.qrCodeType, let qrCodeType = QRCodeType(rawValue: qrCodeTypeString) { HStack(spacing: 4) { Image(systemName: qrCodeType.icon) .font(.system(size: 12)) Text(qrCodeType.displayName) .font(.caption) } .padding(.horizontal, 8) .padding(.vertical, 2) .background(Color.orange.opacity(0.1)) .foregroundColor(.orange) .cornerRadius(8) } Spacer() // 时间 if let createdAt = item.createdAt { Text(formatDate(createdAt)) .font(.caption) .foregroundColor(.gray) } } } Spacer() } .padding(.vertical, 8) .swipeActions(edge: .trailing, allowsFullSwipe: false) { Button("删除", role: .destructive) { onDelete() } } .onTapGesture { if item.dataType == DataType.qrcode.rawValue { showingDetail = true } } .sheet(isPresented: $showingDetail) { if item.dataType == DataType.qrcode.rawValue { NavigationView { QRCodeDetailView(historyItem: item) } } } } private func formatDate(_ date: Date) -> String { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .short return formatter.string(from: date) } }