You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
13 KiB
387 lines
13 KiB
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()
|
|
}
|
|
.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
|
|
|
|
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()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func formatDate(_ date: Date) -> String {
|
|
let formatter = DateFormatter()
|
|
formatter.dateStyle = .short
|
|
formatter.timeStyle = .short
|
|
return formatter.string(from: date)
|
|
}
|
|
} |