137 lines
5.3 KiB
Swift
137 lines
5.3 KiB
Swift
import SwiftUI
|
|
import SwiftData
|
|
|
|
/// 历史记录页 - 对应 Android 版 HistoryActivity
|
|
struct HistoryView: View {
|
|
/// 按最近播放时间倒序展示历史记录。
|
|
@Query(sort: \VodRecord.updateTime, order: .reverse)
|
|
private var records: [VodRecord]
|
|
/// SwiftData 上下文,用于删除单条记录或清空历史。
|
|
@Environment(\.modelContext) private var modelContext
|
|
|
|
#if os(iOS)
|
|
/// iOS 网格配置。
|
|
private let columns = [
|
|
GridItem(.adaptive(minimum: 120, maximum: 160), spacing: 12)
|
|
]
|
|
#else
|
|
/// macOS 网格配置。
|
|
private let columns = [
|
|
GridItem(.adaptive(minimum: 140, maximum: 180), spacing: 16)
|
|
]
|
|
#endif
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Group {
|
|
if records.isEmpty {
|
|
emptyState
|
|
} else {
|
|
ScrollView {
|
|
LazyVGrid(columns: columns, spacing: 16) {
|
|
// 记录卡片支持跳转详情与右键删除。
|
|
ForEach(records) { item in
|
|
NavigationLink(value: movieVideo(from: item)) {
|
|
recordCard(item)
|
|
}
|
|
.buttonStyle(.plain)
|
|
.contextMenu {
|
|
Button(role: .destructive) {
|
|
modelContext.delete(item)
|
|
do {
|
|
try modelContext.save()
|
|
} catch {
|
|
print("删除历史记录失败: \(error)")
|
|
}
|
|
} label: {
|
|
Label("删除记录", systemImage: "trash")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 20)
|
|
.padding(.vertical, 12)
|
|
}
|
|
}
|
|
}
|
|
.background(Color(red: 0.08, green: 0.08, blue: 0.1))
|
|
.navigationTitle("历史记录")
|
|
#if os(iOS)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
#endif
|
|
.toolbar {
|
|
if !records.isEmpty {
|
|
ToolbarItem(placement: .automatic) {
|
|
// 清空历史使用统一缓存服务,确保行为与其他入口一致。
|
|
Button {
|
|
Task {
|
|
CacheStore.shared.clearHistory(context: modelContext)
|
|
}
|
|
} label: {
|
|
Text("清空")
|
|
.foregroundColor(.orange)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 与首页/搜索/收藏共用同一种详情路由模型。
|
|
.navigationDestination(for: Movie.Video.self) { video in
|
|
DetailView(video: video)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 无历史时的占位视图。
|
|
private var emptyState: some View {
|
|
EmptyStateView(
|
|
icon: "clock.arrow.circlepath",
|
|
title: "暂无播放记录",
|
|
message: "您还没有看任何视频,赶快去首页探索吧!"
|
|
)
|
|
.padding(40)
|
|
}
|
|
|
|
/// 历史卡片。
|
|
/// 除海报和标题外,额外显示播放进度与更新时间,便于快速续播。
|
|
private func recordCard(_ item: VodRecord) -> some View {
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
ZStack(alignment: .bottomLeading) {
|
|
CachedAsyncImage(url: URL.posterURL(from: item.vodPic)) { image in
|
|
image.resizable().aspectRatio(2/3, contentMode: .fill)
|
|
} placeholder: {
|
|
Rectangle().fill(Color.gray.opacity(0.3))
|
|
.aspectRatio(2/3, contentMode: .fill)
|
|
.overlay(Image(systemName: "film").foregroundColor(.gray))
|
|
}
|
|
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
|
|
// 播放进度标签
|
|
if !item.playNote.isEmpty {
|
|
Text(item.playNote)
|
|
.font(.system(size: 9))
|
|
.foregroundColor(.white)
|
|
.padding(.horizontal, 6)
|
|
.padding(.vertical, 3)
|
|
.background(Color.black.opacity(0.7))
|
|
.cornerRadius(4)
|
|
.padding(4)
|
|
}
|
|
}
|
|
|
|
Text(item.vodName)
|
|
.font(.caption)
|
|
.foregroundColor(.white)
|
|
.lineLimit(2)
|
|
|
|
Text(item.updateTime.displayString)
|
|
.font(.system(size: 10))
|
|
.foregroundColor(.gray)
|
|
}
|
|
}
|
|
|
|
/// 将历史记录转换为详情页的入参模型。
|
|
private func movieVideo(from item: VodRecord) -> Movie.Video {
|
|
Movie.Video(id: item.vodId, name: item.vodName, pic: item.vodPic, sourceKey: item.sourceKey)
|
|
}
|
|
}
|