Files
Aliyun-VOD-Media-Library-Ma…/apps/macos/VODManager/VODManager/Views/DownloadTaskListView.swift
T
William d94054dd1a 迁移:SPM → Xcode 构建,String Catalog 替代 .strings,清理 web/server/docker 废弃代码
- 项目从 SPM (Sources/VODManager/) 迁移到 Xcode 构建系统 (VODManager.xcodeproj)
- 源码目录从 Sources/VODManager/ 迁移至 VODManager/VODManager/
- 用 Localizable.xcstrings 替代旧的 .lproj/Localizable.strings 文件
- 删除已废弃的 apps/web、apps/server、docker 目录及相关文档
- RTL 侧边栏修复:切语言时用 .id() 强制重建 NavigationSplitView
- 视频库无账号时不发起 API 请求和重试
- 无账号时显示 ContentUnavailableView 提示
2026-06-30 15:46:06 +06:00

120 lines
3.8 KiB
Swift

import SwiftUI
import SwiftData
struct DownloadTaskListView: View {
@EnvironmentObject var accountStore: AccountStore
@Query(sort: \DownloadTask.createdAt, order: .reverse) private var tasks: [DownloadTask]
var body: some View {
VStack(spacing: 0) {
if tasks.isEmpty {
ContentUnavailableView(
"download_task.empty_title",
systemImage: "arrow.down.circle",
description: Text("download_task.empty_desc")
)
} else {
HStack {
Text(verbatim: String(format: String(localized: "download_task.total"), tasks.count))
.foregroundStyle(.secondary)
Spacer()
Button("download_task.clear_completed") {
Task { await DatabaseManager.shared.clearCompletedDownloadTasks() }
}
.buttonStyle(.bordered)
}
.padding(.horizontal)
.padding(.vertical, 8)
List {
ForEach(tasks) { task in
DownloadTaskRow(task: task)
}
}
}
}
.navigationTitle("nav.download_tasks")
}
}
struct DownloadTaskRow: View {
let task: DownloadTask
@State private var showDeleteConfirm = false
var body: some View {
HStack {
statusIcon
VStack(alignment: .leading) {
Text(task.title)
.font(.headline)
}
Spacer()
statusText
if task.status == .completed {
Button("btn.open") {
if let path = task.localPath {
NSWorkspace.shared.selectFile(path, inFileViewerRootedAtPath: "")
}
}
.buttonStyle(.bordered)
.controlSize(.small)
}
}
.padding(.vertical, 4)
.swipeActions(edge: .trailing) {
Button {
showDeleteConfirm = true
} label: {
Label("btn.delete", systemImage: "trash")
}
}
.alert("download_task.delete_confirm_title", isPresented: $showDeleteConfirm) {
Button("btn.cancel", role: .cancel) {}
Button("btn.delete", role: .destructive) {
Task { await DatabaseManager.shared.deleteDownloadTask(task) }
}
} message: {
Text(verbatim: String(format: String(localized: "download_task.delete_confirm_msg"), task.title))
}
}
@ViewBuilder
private var statusIcon: some View {
switch task.status {
case .pending:
Image(systemName: "clock.fill")
.foregroundColor(.gray)
case .downloading:
Image(systemName: "arrow.down.circle.fill")
.foregroundColor(.blue)
case .completed:
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
case .failed:
Image(systemName: "exclamationmark.circle.fill")
.foregroundColor(.red)
}
}
@ViewBuilder
private var statusText: some View {
switch task.status {
case .pending:
Text("download_task.status.pending")
case .downloading:
ProgressView(value: task.progress)
.frame(width: 80)
case .completed:
Text("download_task.status.completed")
case .failed:
VStack(alignment: .trailing) {
Text("download_task.status.failed")
Text(task.errorMessage ?? "")
.font(.caption2)
.foregroundStyle(.red)
}
}
}
}