|
|
import SwiftUI
|
|
|
import CoreData
|
|
|
import Combine
|
|
|
|
|
|
struct HistoryView: View {
|
|
|
@EnvironmentObject var coreDataManager: CoreDataManager
|
|
|
@EnvironmentObject var languageManager: LanguageManager
|
|
|
@State private var searchText = ""
|
|
|
@State private var selectedFilter: HistoryFilter = .all
|
|
|
|
|
|
@State private var itemToDelete: HistoryItem?
|
|
|
@State private var showingDeleteAlert = false
|
|
|
@State private var showingClearConfirmSheet = false
|
|
|
@State private var allHistoryItems: [HistoryItem] = []
|
|
|
@State private var currentPage = 0
|
|
|
@State private var isLoadingMore = false
|
|
|
@State private var hasMoreData = true
|
|
|
@State private var isLoading = false
|
|
|
@State private var refreshTrigger = false
|
|
|
@State private var isBatchDeleteMode = false
|
|
|
@State private var selectedItemsForDelete: Set<UUID> = []
|
|
|
|
|
|
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 "all".localized
|
|
|
case .barcode:
|
|
|
return "barcode".localized
|
|
|
case .qrcode:
|
|
|
return "qrcode".localized
|
|
|
case .scanned:
|
|
|
return "scanned".localized
|
|
|
case .created:
|
|
|
return "created".localized
|
|
|
case .favorites:
|
|
|
return "favorites".localized
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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] {
|
|
|
// 使用 refreshTrigger 强制触发计算属性更新
|
|
|
let _ = refreshTrigger
|
|
|
|
|
|
let searchResults = allHistoryItems.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 {
|
|
|
VStack(spacing: 0) {
|
|
|
// 搜索栏
|
|
|
searchBar
|
|
|
|
|
|
// 过滤器
|
|
|
filterBar
|
|
|
|
|
|
// 内容列表
|
|
|
if filteredItems.isEmpty {
|
|
|
emptyStateView
|
|
|
} else {
|
|
|
historyList
|
|
|
}
|
|
|
}
|
|
|
.navigationTitle("history_records".localized)
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
.navigationBarTitleDisplayMode(.large)
|
|
|
.toolbar {
|
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
|
HStack(spacing: 16) {
|
|
|
if isBatchDeleteMode {
|
|
|
// 批量删除模式下的按钮
|
|
|
Button(action: {
|
|
|
// 全选/取消全选
|
|
|
if selectedItemsForDelete.count == filteredItems.count {
|
|
|
selectedItemsForDelete.removeAll()
|
|
|
} else {
|
|
|
selectedItemsForDelete = Set(filteredItems.compactMap { $0.id })
|
|
|
}
|
|
|
}) {
|
|
|
Image(systemName: selectedItemsForDelete.count == filteredItems.count ? "checkmark.rectangle.fill" : "rectangle.on.rectangle")
|
|
|
.foregroundColor(.blue)
|
|
|
}
|
|
|
|
|
|
Button(action: {
|
|
|
if !selectedItemsForDelete.isEmpty {
|
|
|
deleteSelectedItems()
|
|
|
}
|
|
|
}) {
|
|
|
Image(systemName: "trash.fill")
|
|
|
.foregroundColor(.red)
|
|
|
}
|
|
|
.disabled(selectedItemsForDelete.isEmpty)
|
|
|
|
|
|
// 返回正常状态按钮
|
|
|
Button(action: {
|
|
|
exitBatchDeleteMode()
|
|
|
}) {
|
|
|
Image(systemName: "xmark.circle")
|
|
|
.foregroundColor(.gray)
|
|
|
}
|
|
|
} else {
|
|
|
// 正常模式下的按钮
|
|
|
// 只有当有记录时才显示删除按钮
|
|
|
if !allHistoryItems.isEmpty {
|
|
|
Button(action: {
|
|
|
enterBatchDeleteMode()
|
|
|
}) {
|
|
|
Image(systemName: "trash")
|
|
|
.foregroundColor(.red)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
NavigationLink(destination: CodeTypeSelectionView()) {
|
|
|
Image(systemName: "plus")
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.sheet(isPresented: $showingClearConfirmSheet) {
|
|
|
ClearHistoryConfirmView(
|
|
|
isPresented: $showingClearConfirmSheet,
|
|
|
onConfirm: clearHistory
|
|
|
)
|
|
|
}
|
|
|
.alert("delete_confirmation".localized, isPresented: $showingDeleteAlert) {
|
|
|
Button("cancel".localized, role: .cancel) { }
|
|
|
Button("delete".localized, role: .destructive) {
|
|
|
if let item = itemToDelete {
|
|
|
deleteHistoryItem(item)
|
|
|
itemToDelete = nil
|
|
|
}
|
|
|
}
|
|
|
} message: {
|
|
|
if let item = itemToDelete {
|
|
|
Text(String(format: "confirm_delete_record".localized, item.content ?? ""))
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
}
|
|
|
}
|
|
|
.onAppear {
|
|
|
loadHistoryItems()
|
|
|
}
|
|
|
.onReceive(coreDataManager.objectWillChange) { _ in
|
|
|
// 当Core Data数据发生变化时,重新加载历史记录
|
|
|
loadHistoryItems()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// MARK: - 加载历史记录
|
|
|
private func loadHistoryItems() {
|
|
|
isLoading = true
|
|
|
currentPage = 0
|
|
|
hasMoreData = true
|
|
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
|
let items = coreDataManager.fetchHistoryItems(page: currentPage)
|
|
|
allHistoryItems = items
|
|
|
|
|
|
// 检查是否还有更多数据
|
|
|
let nextPageItems = coreDataManager.fetchHistoryItems(page: currentPage + 1)
|
|
|
hasMoreData = !nextPageItems.isEmpty
|
|
|
|
|
|
isLoading = false
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private func loadMoreHistoryItems() {
|
|
|
guard !isLoadingMore && hasMoreData else { return }
|
|
|
|
|
|
isLoadingMore = true
|
|
|
currentPage += 1
|
|
|
|
|
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
|
let newItems = coreDataManager.fetchHistoryItems(page: currentPage)
|
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
if newItems.isEmpty {
|
|
|
self.hasMoreData = false
|
|
|
} else {
|
|
|
self.allHistoryItems.append(contentsOf: newItems)
|
|
|
|
|
|
// 检查是否还有更多数据
|
|
|
let nextPageItems = coreDataManager.fetchHistoryItems(page: self.currentPage + 1)
|
|
|
self.hasMoreData = !nextPageItems.isEmpty
|
|
|
}
|
|
|
self.isLoadingMore = false
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// MARK: - 过滤器操作
|
|
|
private func filterAction(for filter: HistoryFilter) {
|
|
|
// 直接切换过滤器,无任何延迟
|
|
|
selectedFilter = filter
|
|
|
}
|
|
|
|
|
|
// MARK: - 清空历史记录
|
|
|
private func clearHistory() {
|
|
|
coreDataManager.clearAllHistory()
|
|
|
allHistoryItems.removeAll()
|
|
|
refreshTrigger.toggle()
|
|
|
}
|
|
|
|
|
|
// MARK: - 切换收藏状态
|
|
|
private func toggleFavorite(_ item: HistoryItem) {
|
|
|
// 先保存到 Core Data
|
|
|
item.isFavorite.toggle()
|
|
|
coreDataManager.save()
|
|
|
|
|
|
// 更新本地缓存,避免重新加载数据
|
|
|
if let index = allHistoryItems.firstIndex(where: { $0.id == item.id }) {
|
|
|
allHistoryItems[index].isFavorite = item.isFavorite
|
|
|
}
|
|
|
|
|
|
// 强制触发视图刷新
|
|
|
refreshTrigger.toggle()
|
|
|
}
|
|
|
|
|
|
// MARK: - 删除历史记录
|
|
|
private func deleteHistoryItem(_ item: HistoryItem) {
|
|
|
coreDataManager.deleteHistoryItem(item)
|
|
|
// 从本地缓存中移除
|
|
|
allHistoryItems.removeAll { $0.id == item.id }
|
|
|
refreshTrigger.toggle()
|
|
|
}
|
|
|
|
|
|
// MARK: - 显示删除确认
|
|
|
private func showDeleteConfirmation(for item: HistoryItem) {
|
|
|
itemToDelete = item
|
|
|
showingDeleteAlert = true
|
|
|
}
|
|
|
|
|
|
// MARK: - 批量删除相关方法
|
|
|
private func enterBatchDeleteMode() {
|
|
|
isBatchDeleteMode = true
|
|
|
// 默认全选当前过滤后的项目
|
|
|
selectedItemsForDelete = Set(filteredItems.compactMap { $0.id })
|
|
|
}
|
|
|
|
|
|
private func exitBatchDeleteMode() {
|
|
|
isBatchDeleteMode = false
|
|
|
selectedItemsForDelete.removeAll()
|
|
|
}
|
|
|
|
|
|
private func deleteSelectedItems() {
|
|
|
// 获取选中的项目
|
|
|
let itemsToDelete = allHistoryItems.filter { item in
|
|
|
guard let id = item.id else { return false }
|
|
|
return selectedItemsForDelete.contains(id)
|
|
|
}
|
|
|
|
|
|
// 批量删除
|
|
|
for item in itemsToDelete {
|
|
|
coreDataManager.deleteHistoryItem(item)
|
|
|
}
|
|
|
|
|
|
// 从本地缓存中移除
|
|
|
allHistoryItems.removeAll { item in
|
|
|
guard let id = item.id else { return false }
|
|
|
return selectedItemsForDelete.contains(id)
|
|
|
}
|
|
|
|
|
|
// 清空选择状态并退出批量删除模式
|
|
|
selectedItemsForDelete.removeAll()
|
|
|
isBatchDeleteMode = false
|
|
|
|
|
|
// 强制刷新数据
|
|
|
DispatchQueue.main.async {
|
|
|
allHistoryItems = coreDataManager.fetchHistoryItems()
|
|
|
refreshTrigger.toggle()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// MARK: - 搜索栏
|
|
|
private var searchBar: some View {
|
|
|
HStack {
|
|
|
Image(systemName: "magnifyingglass")
|
|
|
.foregroundColor(.gray)
|
|
|
|
|
|
TextField("search_history_records".localized, 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,
|
|
|
isLoading: false,
|
|
|
action: {
|
|
|
filterAction(for: filter)
|
|
|
}
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
.padding(.horizontal)
|
|
|
}
|
|
|
.padding(.vertical, 8)
|
|
|
.background(Color(.systemBackground))
|
|
|
}
|
|
|
|
|
|
// MARK: - 历史记录列表
|
|
|
private var historyList: some View {
|
|
|
List {
|
|
|
if isLoading {
|
|
|
HStack {
|
|
|
Spacer()
|
|
|
VStack(spacing: 16) {
|
|
|
ProgressView()
|
|
|
.scaleEffect(1.2)
|
|
|
Text("loading".localized)
|
|
|
.font(.caption)
|
|
|
.foregroundColor(.secondary)
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
}
|
|
|
.padding(.vertical, 40)
|
|
|
Spacer()
|
|
|
}
|
|
|
} else {
|
|
|
ForEach(filteredItems) { item in
|
|
|
HistoryItemRow(
|
|
|
item: item,
|
|
|
onToggleFavorite: {
|
|
|
toggleFavorite(item)
|
|
|
},
|
|
|
onDelete: {
|
|
|
showDeleteConfirmation(for: item)
|
|
|
},
|
|
|
isBatchDeleteMode: isBatchDeleteMode,
|
|
|
isSelected: selectedItemsForDelete.contains(item.id ?? UUID()),
|
|
|
onToggleSelection: {
|
|
|
if let id = item.id {
|
|
|
if selectedItemsForDelete.contains(id) {
|
|
|
selectedItemsForDelete.remove(id)
|
|
|
} else {
|
|
|
selectedItemsForDelete.insert(id)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
)
|
|
|
}
|
|
|
|
|
|
// 加载更多按钮
|
|
|
if hasMoreData && !isLoadingMore {
|
|
|
Button(action: loadMoreHistoryItems) {
|
|
|
HStack {
|
|
|
Image(systemName: "arrow.down.circle")
|
|
|
Text("load_more".localized)
|
|
|
}
|
|
|
.foregroundColor(.blue)
|
|
|
.padding()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 加载更多指示器
|
|
|
if isLoadingMore {
|
|
|
HStack {
|
|
|
Spacer()
|
|
|
ProgressView()
|
|
|
.scaleEffect(0.8)
|
|
|
Text("loading_more".localized)
|
|
|
.font(.caption)
|
|
|
.foregroundColor(.secondary)
|
|
|
Spacer()
|
|
|
}
|
|
|
.padding()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
.listStyle(PlainListStyle())
|
|
|
}
|
|
|
|
|
|
// MARK: - 空状态视图
|
|
|
private var emptyStateView: some View {
|
|
|
VStack(spacing: 20) {
|
|
|
Image(systemName: "clock.arrow.circlepath")
|
|
|
.font(.system(size: 60))
|
|
|
.foregroundColor(.gray)
|
|
|
|
|
|
Text("no_history_records".localized)
|
|
|
.font(.title2)
|
|
|
.fontWeight(.medium)
|
|
|
.foregroundColor(.gray)
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
|
|
|
Text("scan_or_create_to_start".localized)
|
|
|
.font(.body)
|
|
|
.foregroundColor(.gray)
|
|
|
.multilineTextAlignment(.center)
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
|
|
|
NavigationLink(destination: CodeTypeSelectionView()) {
|
|
|
HStack {
|
|
|
Image(systemName: "plus.circle.fill")
|
|
|
Text("create_first_record".localized)
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
}
|
|
|
.font(.headline)
|
|
|
.foregroundColor(.white)
|
|
|
.padding()
|
|
|
.background(Color.blue)
|
|
|
.cornerRadius(10)
|
|
|
}
|
|
|
}
|
|
|
.padding()
|
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#Preview {
|
|
|
NavigationView {
|
|
|
HistoryView()
|
|
|
.environmentObject(LanguageManager.shared)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// MARK: - 过滤器芯片
|
|
|
struct FilterChip: View {
|
|
|
let filter: HistoryView.HistoryFilter
|
|
|
let isSelected: Bool
|
|
|
let isLoading: 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(
|
|
|
RoundedRectangle(cornerRadius: 20)
|
|
|
.fill(isSelected ? Color.blue : Color(.systemGray5))
|
|
|
)
|
|
|
.foregroundColor(isSelected ? .white : .primary)
|
|
|
.scaleEffect(isSelected ? 1.05 : 1.0)
|
|
|
}
|
|
|
.buttonStyle(OptimizedFilterChipButtonStyle(isSelected: isSelected))
|
|
|
.animation(.easeInOut(duration: 0.2), value: isSelected)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// MARK: - 优化的过滤器芯片按钮样式
|
|
|
struct OptimizedFilterChipButtonStyle: ButtonStyle {
|
|
|
let isSelected: Bool
|
|
|
|
|
|
func makeBody(configuration: Configuration) -> some View {
|
|
|
configuration.label
|
|
|
.scaleEffect(configuration.isPressed ? 0.95 : 1.0)
|
|
|
.opacity(configuration.isPressed ? 0.9 : 1.0)
|
|
|
.animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// MARK: - 过滤器芯片按钮样式(保留以防向后兼容)
|
|
|
struct FilterChipButtonStyle: ButtonStyle {
|
|
|
func makeBody(configuration: Configuration) -> some View {
|
|
|
configuration.label
|
|
|
.scaleEffect(configuration.isPressed ? 0.95 : 1.0)
|
|
|
.opacity(configuration.isPressed ? 0.8 : 1.0)
|
|
|
.animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// MARK: - 历史记录项行
|
|
|
struct HistoryItemRow: View {
|
|
|
let item: HistoryItem
|
|
|
let onToggleFavorite: () -> Void
|
|
|
let onDelete: () -> Void
|
|
|
let isBatchDeleteMode: Bool
|
|
|
let isSelected: Bool
|
|
|
let onToggleSelection: () -> Void
|
|
|
|
|
|
var body: some View {
|
|
|
HStack(spacing: 12) {
|
|
|
// 批量删除模式下的选择框
|
|
|
if isBatchDeleteMode {
|
|
|
Button(action: onToggleSelection) {
|
|
|
Image(systemName: isSelected ? "checkmark.square.fill" : "square")
|
|
|
.font(.system(size: 20))
|
|
|
.foregroundColor(isSelected ? .blue : .gray)
|
|
|
}
|
|
|
.buttonStyle(PlainButtonStyle())
|
|
|
}
|
|
|
|
|
|
// 类型图标
|
|
|
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("delete".localized, role: .destructive) {
|
|
|
onDelete()
|
|
|
}
|
|
|
}
|
|
|
.background(
|
|
|
// 根据数据类型添加导航链接
|
|
|
Group {
|
|
|
if item.dataType == DataType.qrcode.rawValue {
|
|
|
NavigationLink(
|
|
|
destination: QRCodeDetailView(historyItem: item),
|
|
|
label: { EmptyView() }
|
|
|
)
|
|
|
} else if item.dataType == DataType.barcode.rawValue {
|
|
|
NavigationLink(
|
|
|
destination: BarcodeDetailView(historyItem: item),
|
|
|
label: { EmptyView() }
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
)
|
|
|
}
|
|
|
|
|
|
private func formatDate(_ date: Date) -> String {
|
|
|
let formatter = DateFormatter()
|
|
|
formatter.dateStyle = .short
|
|
|
formatter.timeStyle = .short
|
|
|
return formatter.string(from: date)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// MARK: - 清空历史记录确认视图
|
|
|
struct ClearHistoryConfirmView: View {
|
|
|
@EnvironmentObject var languageManager: LanguageManager
|
|
|
@Binding var isPresented: Bool
|
|
|
let onConfirm: () -> Void
|
|
|
|
|
|
var body: some View {
|
|
|
NavigationView {
|
|
|
VStack(spacing: 20) {
|
|
|
// 警告图标
|
|
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
|
.font(.system(size: 50))
|
|
|
.foregroundColor(.red)
|
|
|
|
|
|
// 标题
|
|
|
Text("clear_history".localized)
|
|
|
.font(.title2)
|
|
|
.fontWeight(.bold)
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
|
|
|
// 简单说明
|
|
|
Text("clear_history_warning".localized)
|
|
|
.font(.body)
|
|
|
.foregroundColor(.secondary)
|
|
|
.multilineTextAlignment(.center)
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
|
|
|
Spacer()
|
|
|
|
|
|
// 按钮区域
|
|
|
VStack(spacing: 12) {
|
|
|
// 确认删除按钮
|
|
|
Button(action: {
|
|
|
onConfirm()
|
|
|
isPresented = false
|
|
|
}) {
|
|
|
HStack {
|
|
|
Image(systemName: "trash.fill")
|
|
|
Text("confirm_delete".localized)
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
}
|
|
|
.frame(maxWidth: .infinity)
|
|
|
.padding()
|
|
|
.background(Color.red)
|
|
|
.foregroundColor(.white)
|
|
|
.cornerRadius(10)
|
|
|
}
|
|
|
|
|
|
// 取消按钮
|
|
|
Button(action: {
|
|
|
isPresented = false
|
|
|
}) {
|
|
|
Text("cancel".localized)
|
|
|
.frame(maxWidth: .infinity)
|
|
|
.padding()
|
|
|
.background(Color(.systemGray5))
|
|
|
.foregroundColor(.primary)
|
|
|
.cornerRadius(10)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
.padding(20)
|
|
|
.navigationTitle("confirm_delete".localized)
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
|
.navigationBarBackButtonHidden(true)
|
|
|
.toolbar {
|
|
|
ToolbarItem(placement: .navigationBarLeading) {
|
|
|
Button("close".localized) {
|
|
|
isPresented = false
|
|
|
}
|
|
|
.id(languageManager.refreshTrigger)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|