2024-10-31 22:35:22 +08:00
|
|
|
//
|
2024-11-05 20:30:18 +08:00
|
|
|
// Adobe Downloader
|
2024-10-31 22:35:22 +08:00
|
|
|
//
|
|
|
|
|
// Created by X1a0He on 2024/10/30.
|
|
|
|
|
//
|
|
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
|
|
struct InstallProgressView: View {
|
2024-11-04 17:40:01 +08:00
|
|
|
@EnvironmentObject private var networkManager: NetworkManager
|
2024-10-31 22:35:22 +08:00
|
|
|
let productName: String
|
|
|
|
|
let progress: Double
|
|
|
|
|
let status: String
|
|
|
|
|
let onCancel: () -> Void
|
2024-11-04 00:29:08 +08:00
|
|
|
let onRetry: (() -> Void)?
|
2025-03-27 01:05:20 +08:00
|
|
|
let errorDetails: String?
|
|
|
|
|
|
|
|
|
|
init(productName: String,
|
|
|
|
|
progress: Double,
|
|
|
|
|
status: String,
|
|
|
|
|
onCancel: @escaping () -> Void,
|
|
|
|
|
onRetry: (() -> Void)? = nil,
|
|
|
|
|
errorDetails: String? = nil) {
|
|
|
|
|
self.productName = productName
|
|
|
|
|
self.progress = progress
|
|
|
|
|
self.status = status
|
|
|
|
|
self.onCancel = onCancel
|
|
|
|
|
self.onRetry = onRetry
|
|
|
|
|
self.errorDetails = errorDetails
|
|
|
|
|
}
|
2024-10-31 22:35:22 +08:00
|
|
|
|
|
|
|
|
private var isCompleted: Bool {
|
2024-11-07 16:14:42 +08:00
|
|
|
progress >= 1.0 || status == String(localized: "安装完成")
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
|
|
|
|
|
2024-11-01 17:28:23 +08:00
|
|
|
private var isFailed: Bool {
|
2024-11-15 17:47:15 +08:00
|
|
|
status.contains(String(localized: "安装失败"))
|
2024-11-01 17:28:23 +08:00
|
|
|
}
|
|
|
|
|
|
2024-10-31 22:35:22 +08:00
|
|
|
private var progressText: String {
|
|
|
|
|
if isCompleted {
|
2024-11-07 16:14:42 +08:00
|
|
|
return String(localized: "安装完成")
|
2024-10-31 22:35:22 +08:00
|
|
|
} else {
|
|
|
|
|
return "\(Int(progress * 100))%"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-05 09:22:40 +08:00
|
|
|
private var statusIcon: String {
|
|
|
|
|
if isCompleted {
|
|
|
|
|
return "checkmark.circle.fill"
|
|
|
|
|
} else if isFailed {
|
|
|
|
|
return "xmark.circle.fill"
|
|
|
|
|
} else {
|
|
|
|
|
return "arrow.down.circle.fill"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private var statusColor: Color {
|
|
|
|
|
if isCompleted {
|
|
|
|
|
return .green
|
|
|
|
|
} else if isFailed {
|
|
|
|
|
return .red
|
|
|
|
|
} else {
|
|
|
|
|
return .blue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private var statusTitle: String {
|
|
|
|
|
if isCompleted {
|
2024-11-07 16:14:42 +08:00
|
|
|
return String(localized: "\(productName) 安装完成")
|
2024-11-05 09:22:40 +08:00
|
|
|
} else if isFailed {
|
2024-11-07 16:14:42 +08:00
|
|
|
return String(localized: "\(productName) 安装失败")
|
2024-11-05 09:22:40 +08:00
|
|
|
} else {
|
2024-11-07 16:14:42 +08:00
|
|
|
return String(localized: "正在安装 \(productName)")
|
2024-11-05 09:22:40 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 22:35:22 +08:00
|
|
|
var body: some View {
|
|
|
|
|
VStack(spacing: 16) {
|
|
|
|
|
HStack {
|
2024-11-05 09:22:40 +08:00
|
|
|
Image(systemName: statusIcon)
|
2024-10-31 22:35:22 +08:00
|
|
|
.font(.title2)
|
2024-11-05 09:22:40 +08:00
|
|
|
.foregroundColor(statusColor)
|
2024-10-31 22:35:22 +08:00
|
|
|
|
2024-11-05 09:22:40 +08:00
|
|
|
Text(statusTitle)
|
2024-10-31 22:35:22 +08:00
|
|
|
.font(.headline)
|
|
|
|
|
}
|
2024-11-04 17:40:01 +08:00
|
|
|
.padding(.horizontal, 20)
|
2024-10-31 22:35:22 +08:00
|
|
|
|
2024-11-05 09:22:40 +08:00
|
|
|
if !isFailed {
|
|
|
|
|
ProgressSection(progress: progress, progressText: progressText)
|
2025-03-09 00:41:17 +08:00
|
|
|
.padding(.vertical, 4)
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
2024-11-05 09:22:40 +08:00
|
|
|
|
|
|
|
|
if isFailed {
|
2024-11-07 16:14:42 +08:00
|
|
|
ErrorSection(
|
2024-11-15 17:47:15 +08:00
|
|
|
status: status,
|
2025-03-27 01:05:20 +08:00
|
|
|
isFailed: true,
|
|
|
|
|
errorDetails: errorDetails
|
2024-11-07 16:14:42 +08:00
|
|
|
)
|
2024-11-05 09:22:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ButtonSection(
|
|
|
|
|
isCompleted: isCompleted,
|
|
|
|
|
isFailed: isFailed,
|
|
|
|
|
onCancel: onCancel,
|
|
|
|
|
onRetry: onRetry
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
.padding()
|
2025-03-27 01:05:20 +08:00
|
|
|
.frame(minWidth: 600)
|
2025-03-09 00:41:17 +08:00
|
|
|
.background(
|
|
|
|
|
RoundedRectangle(cornerRadius: 10)
|
2025-03-27 01:05:20 +08:00
|
|
|
.fill(Color(NSColor.clear))
|
2025-03-09 00:41:17 +08:00
|
|
|
)
|
2024-11-05 09:22:40 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private struct ProgressSection: View {
|
|
|
|
|
let progress: Double
|
|
|
|
|
let progressText: String
|
|
|
|
|
|
|
|
|
|
var body: some View {
|
2025-03-09 00:41:17 +08:00
|
|
|
VStack(spacing: 8) {
|
|
|
|
|
HStack {
|
|
|
|
|
Spacer()
|
|
|
|
|
|
|
|
|
|
Text(progressText)
|
|
|
|
|
.font(.system(size: 12, weight: .bold))
|
|
|
|
|
.foregroundColor(.blue)
|
|
|
|
|
.padding(.vertical, 2)
|
|
|
|
|
.padding(.horizontal, 8)
|
|
|
|
|
.background(Color.blue.opacity(0.1))
|
|
|
|
|
.cornerRadius(4)
|
|
|
|
|
}
|
2024-11-04 17:40:01 +08:00
|
|
|
|
2025-03-09 00:41:17 +08:00
|
|
|
GeometryReader { geometry in
|
|
|
|
|
ZStack(alignment: .leading) {
|
|
|
|
|
Rectangle()
|
|
|
|
|
.fill(Color.secondary.opacity(0.1))
|
|
|
|
|
.frame(height: 6)
|
|
|
|
|
.cornerRadius(3)
|
|
|
|
|
|
|
|
|
|
Rectangle()
|
|
|
|
|
.fill(
|
|
|
|
|
LinearGradient(
|
|
|
|
|
gradient: Gradient(colors: [Color.blue.opacity(0.7), Color.blue]),
|
|
|
|
|
startPoint: .leading,
|
|
|
|
|
endPoint: .trailing
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.frame(width: max(0, CGFloat(progress) * geometry.size.width), height: 6)
|
|
|
|
|
.cornerRadius(3)
|
|
|
|
|
.animation(.linear(duration: 0.3), value: progress)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.frame(height: 6)
|
2024-11-05 09:22:40 +08:00
|
|
|
}
|
|
|
|
|
.padding(.horizontal, 20)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private struct ErrorSection: View {
|
|
|
|
|
let status: String
|
2024-11-07 16:14:42 +08:00
|
|
|
let isFailed: Bool
|
2025-03-27 01:05:20 +08:00
|
|
|
let errorDetails: String?
|
|
|
|
|
|
|
|
|
|
init(status: String,
|
|
|
|
|
isFailed: Bool,
|
|
|
|
|
errorDetails: String? = nil) {
|
|
|
|
|
self.status = status
|
|
|
|
|
self.isFailed = isFailed
|
|
|
|
|
self.errorDetails = errorDetails
|
|
|
|
|
}
|
2024-11-07 16:14:42 +08:00
|
|
|
|
|
|
|
|
var body: some View {
|
2025-03-09 00:41:17 +08:00
|
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
|
|
|
HStack(spacing: 6) {
|
|
|
|
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
|
|
|
.foregroundColor(.orange)
|
|
|
|
|
.font(.system(size: 14))
|
|
|
|
|
Text("错误详情")
|
|
|
|
|
.font(.system(size: 13, weight: .medium))
|
|
|
|
|
.foregroundColor(.primary.opacity(0.8))
|
|
|
|
|
}
|
|
|
|
|
.padding(.vertical, 2)
|
|
|
|
|
|
2024-11-07 16:14:42 +08:00
|
|
|
Text(status)
|
2025-03-09 00:41:17 +08:00
|
|
|
.font(.system(size: 12))
|
2024-11-07 16:14:42 +08:00
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
.textSelection(.enabled)
|
|
|
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
2025-03-09 00:41:17 +08:00
|
|
|
.padding(10)
|
|
|
|
|
.background(
|
|
|
|
|
RoundedRectangle(cornerRadius: 8)
|
|
|
|
|
.fill(Color.red.opacity(0.05))
|
|
|
|
|
)
|
|
|
|
|
.overlay(
|
|
|
|
|
RoundedRectangle(cornerRadius: 8)
|
|
|
|
|
.strokeBorder(Color.red.opacity(0.2), lineWidth: 1)
|
|
|
|
|
)
|
|
|
|
|
|
2025-03-27 01:05:20 +08:00
|
|
|
if let errorDetails = errorDetails, !errorDetails.isEmpty {
|
|
|
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
|
|
|
HStack(spacing: 6) {
|
|
|
|
|
Image(systemName: "doc.text.magnifyingglass")
|
|
|
|
|
.foregroundColor(.orange.opacity(0.7))
|
|
|
|
|
.font(.system(size: 14))
|
|
|
|
|
Text("日志详情")
|
|
|
|
|
.font(.system(size: 13, weight: .medium))
|
|
|
|
|
.foregroundColor(.primary.opacity(0.8))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ScrollView {
|
|
|
|
|
Text(errorDetails)
|
|
|
|
|
.font(.system(size: 11, design: .monospaced))
|
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
.textSelection(.enabled)
|
|
|
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
|
|
|
.padding(10)
|
|
|
|
|
.background(
|
|
|
|
|
RoundedRectangle(cornerRadius: 8)
|
|
|
|
|
.fill(Color.orange.opacity(0.05))
|
|
|
|
|
)
|
|
|
|
|
.overlay(
|
|
|
|
|
RoundedRectangle(cornerRadius: 8)
|
|
|
|
|
.strokeBorder(Color.orange.opacity(0.2), lineWidth: 1)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
.frame(maxHeight: 120)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-07 16:14:42 +08:00
|
|
|
if isFailed {
|
2025-03-09 00:41:17 +08:00
|
|
|
HStack(spacing: 6) {
|
|
|
|
|
Image(systemName: "terminal.fill")
|
|
|
|
|
.foregroundColor(.blue.opacity(0.7))
|
|
|
|
|
.font(.system(size: 14))
|
|
|
|
|
Text("自行安装命令")
|
|
|
|
|
.font(.system(size: 13, weight: .medium))
|
|
|
|
|
.foregroundColor(.primary.opacity(0.8))
|
|
|
|
|
Spacer()
|
2024-11-07 16:14:42 +08:00
|
|
|
CommandPopover()
|
|
|
|
|
}
|
2025-03-09 00:41:17 +08:00
|
|
|
.padding(.vertical, 2)
|
2024-11-07 16:14:42 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
|
|
|
.padding(.horizontal, 20)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private struct CommandSection: View {
|
|
|
|
|
let command: String
|
|
|
|
|
|
2024-11-05 09:22:40 +08:00
|
|
|
var body: some View {
|
|
|
|
|
ScrollView {
|
|
|
|
|
VStack(alignment: .leading, spacing: 8) {
|
2024-11-07 16:14:42 +08:00
|
|
|
Text("自行安装命令:")
|
2024-11-05 09:22:40 +08:00
|
|
|
.font(.caption)
|
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
.fontWeight(.medium)
|
|
|
|
|
|
2024-11-07 16:14:42 +08:00
|
|
|
Text(command)
|
2024-11-05 09:22:40 +08:00
|
|
|
.font(.caption)
|
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
.textSelection(.enabled)
|
2024-11-15 17:47:15 +08:00
|
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
2024-11-07 16:14:42 +08:00
|
|
|
.frame(minHeight: 200)
|
2024-11-05 09:22:40 +08:00
|
|
|
.padding(8)
|
|
|
|
|
.background(Color.secondary.opacity(0.1))
|
|
|
|
|
.cornerRadius(6)
|
2024-11-01 17:28:23 +08:00
|
|
|
}
|
2024-11-05 09:22:40 +08:00
|
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-01 17:28:23 +08:00
|
|
|
|
2024-11-05 09:22:40 +08:00
|
|
|
private struct ButtonSection: View {
|
|
|
|
|
let isCompleted: Bool
|
|
|
|
|
let isFailed: Bool
|
|
|
|
|
let onCancel: () -> Void
|
|
|
|
|
let onRetry: (() -> Void)?
|
|
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
|
HStack(spacing: 12) {
|
|
|
|
|
if isFailed {
|
|
|
|
|
if let onRetry = onRetry {
|
|
|
|
|
Button(action: onRetry) {
|
2025-03-09 00:41:17 +08:00
|
|
|
Label("重试", systemImage: "arrow.clockwise")
|
|
|
|
|
.font(.system(size: 13, weight: .medium))
|
|
|
|
|
.foregroundColor(.white)
|
2024-11-01 17:28:23 +08:00
|
|
|
}
|
2025-03-09 00:41:17 +08:00
|
|
|
.buttonStyle(BeautifulButtonStyle(baseColor: .blue))
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
2024-11-05 09:22:40 +08:00
|
|
|
|
|
|
|
|
Button(action: onCancel) {
|
2025-03-09 00:41:17 +08:00
|
|
|
Label("关闭", systemImage: "xmark")
|
|
|
|
|
.font(.system(size: 13, weight: .medium))
|
|
|
|
|
.foregroundColor(.white)
|
2024-11-05 09:22:40 +08:00
|
|
|
}
|
2025-03-09 00:41:17 +08:00
|
|
|
.buttonStyle(BeautifulButtonStyle(baseColor: .red))
|
2024-11-05 09:22:40 +08:00
|
|
|
} else if isCompleted {
|
|
|
|
|
Button(action: onCancel) {
|
2025-03-09 00:41:17 +08:00
|
|
|
Label("关闭", systemImage: "xmark")
|
|
|
|
|
.font(.system(size: 13, weight: .medium))
|
|
|
|
|
.foregroundColor(.white)
|
2024-11-05 09:22:40 +08:00
|
|
|
}
|
2025-03-09 00:41:17 +08:00
|
|
|
.buttonStyle(BeautifulButtonStyle(baseColor: .green))
|
2024-11-05 09:22:40 +08:00
|
|
|
} else {
|
|
|
|
|
Button(action: onCancel) {
|
2025-03-09 00:41:17 +08:00
|
|
|
Label("取消", systemImage: "xmark")
|
|
|
|
|
.font(.system(size: 13, weight: .medium))
|
|
|
|
|
.foregroundColor(.white)
|
2024-11-05 09:22:40 +08:00
|
|
|
}
|
2025-03-09 00:41:17 +08:00
|
|
|
.buttonStyle(BeautifulButtonStyle(baseColor: .red))
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
|
|
|
|
}
|
2024-11-05 09:22:40 +08:00
|
|
|
.padding(.horizontal, 20)
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-07 16:14:42 +08:00
|
|
|
private struct CommandPopover: View {
|
|
|
|
|
@EnvironmentObject private var networkManager: NetworkManager
|
|
|
|
|
@State private var showPopover = false
|
2024-11-07 21:31:29 +08:00
|
|
|
@State private var showCopiedAlert = false
|
2024-11-07 16:14:42 +08:00
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
|
Button(action: { showPopover.toggle() }) {
|
2025-03-09 00:41:17 +08:00
|
|
|
Text("查看")
|
|
|
|
|
.font(.system(size: 12, weight: .medium))
|
|
|
|
|
.foregroundColor(.white)
|
|
|
|
|
.padding(.vertical, 4)
|
|
|
|
|
.padding(.horizontal, 8)
|
|
|
|
|
.background(Color.blue.opacity(0.8))
|
|
|
|
|
.cornerRadius(4)
|
2024-11-07 16:14:42 +08:00
|
|
|
}
|
|
|
|
|
.buttonStyle(.plain)
|
|
|
|
|
.popover(isPresented: $showPopover, arrowEdge: .bottom) {
|
2025-03-09 00:41:17 +08:00
|
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
|
|
|
HStack {
|
|
|
|
|
Text("安装命令")
|
|
|
|
|
.font(.headline)
|
|
|
|
|
.foregroundColor(.primary)
|
|
|
|
|
|
|
|
|
|
Spacer()
|
2024-11-07 21:31:29 +08:00
|
|
|
|
2025-03-09 00:41:17 +08:00
|
|
|
Button(action: {
|
|
|
|
|
let command = networkManager.installCommand
|
|
|
|
|
let pasteboard = NSPasteboard.general
|
|
|
|
|
pasteboard.clearContents()
|
|
|
|
|
pasteboard.setString(command, forType: .string)
|
|
|
|
|
showCopiedAlert = true
|
|
|
|
|
|
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
|
|
|
|
showCopiedAlert = false
|
|
|
|
|
}
|
|
|
|
|
}) {
|
|
|
|
|
Label("复制", systemImage: "doc.on.doc")
|
|
|
|
|
.font(.system(size: 12, weight: .medium))
|
|
|
|
|
.foregroundColor(.white)
|
2024-11-07 21:31:29 +08:00
|
|
|
}
|
2025-03-09 00:41:17 +08:00
|
|
|
.buttonStyle(BeautifulButtonStyle(baseColor: .blue))
|
2024-11-07 21:31:29 +08:00
|
|
|
}
|
2024-11-07 16:14:42 +08:00
|
|
|
|
2024-11-07 21:31:29 +08:00
|
|
|
if showCopiedAlert {
|
2025-03-09 00:41:17 +08:00
|
|
|
HStack {
|
|
|
|
|
Image(systemName: "checkmark.circle.fill")
|
|
|
|
|
.foregroundColor(.green)
|
|
|
|
|
Text("命令已复制到剪贴板")
|
|
|
|
|
.font(.system(size: 12))
|
|
|
|
|
.foregroundColor(.green)
|
|
|
|
|
}
|
|
|
|
|
.padding(6)
|
|
|
|
|
.background(Color.green.opacity(0.1))
|
|
|
|
|
.cornerRadius(4)
|
|
|
|
|
.transition(.opacity)
|
|
|
|
|
.animation(.easeInOut, value: showCopiedAlert)
|
2024-11-07 16:14:42 +08:00
|
|
|
}
|
|
|
|
|
|
2024-11-07 21:31:29 +08:00
|
|
|
let command = networkManager.installCommand
|
|
|
|
|
Text(command)
|
2025-03-09 00:41:17 +08:00
|
|
|
.font(.system(size: 12, design: .monospaced))
|
|
|
|
|
.foregroundColor(.primary.opacity(0.8))
|
2024-11-07 16:14:42 +08:00
|
|
|
.textSelection(.enabled)
|
2025-03-09 00:41:17 +08:00
|
|
|
.padding(12)
|
|
|
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
|
|
|
.background(
|
|
|
|
|
RoundedRectangle(cornerRadius: 8)
|
|
|
|
|
.fill(Color(NSColor.textBackgroundColor))
|
|
|
|
|
)
|
|
|
|
|
.overlay(
|
|
|
|
|
RoundedRectangle(cornerRadius: 8)
|
|
|
|
|
.strokeBorder(Color.secondary.opacity(0.2), lineWidth: 1)
|
|
|
|
|
)
|
2024-11-07 16:14:42 +08:00
|
|
|
}
|
2025-03-09 00:41:17 +08:00
|
|
|
.padding(16)
|
|
|
|
|
.frame(width: 450)
|
2024-11-07 16:14:42 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-14 13:30:13 +08:00
|
|
|
#Preview("安装中") {
|
2024-11-04 17:40:01 +08:00
|
|
|
let networkManager = NetworkManager()
|
|
|
|
|
return InstallProgressView(
|
|
|
|
|
productName: "Adobe Photoshop",
|
|
|
|
|
progress: 0.45,
|
|
|
|
|
status: "正在安装核心组件...",
|
|
|
|
|
onCancel: {},
|
2025-03-27 01:05:20 +08:00
|
|
|
onRetry: nil,
|
|
|
|
|
errorDetails: nil
|
2024-11-04 17:40:01 +08:00
|
|
|
)
|
|
|
|
|
.environmentObject(networkManager)
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-14 13:30:13 +08:00
|
|
|
#Preview("安装失败") {
|
2024-11-04 17:40:01 +08:00
|
|
|
let networkManager = NetworkManager()
|
|
|
|
|
return InstallProgressView(
|
|
|
|
|
productName: "Adobe Photoshop",
|
|
|
|
|
progress: 0.0,
|
|
|
|
|
status: "安装失败: 权限被拒绝",
|
|
|
|
|
onCancel: {},
|
2025-03-27 01:05:20 +08:00
|
|
|
onRetry: {},
|
|
|
|
|
errorDetails: "详细错误日志"
|
2024-11-04 17:40:01 +08:00
|
|
|
)
|
|
|
|
|
.environmentObject(networkManager)
|
|
|
|
|
.onAppear {
|
2024-11-07 16:14:42 +08:00
|
|
|
networkManager.installCommand = "sudo \"/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup\" --install=1 --driverXML=\"/Users/demo/Downloads/Adobe Photoshop/driver.xml\""
|
2024-11-04 17:40:01 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-14 13:30:13 +08:00
|
|
|
#Preview("安装完成") {
|
2024-11-04 17:40:01 +08:00
|
|
|
let networkManager = NetworkManager()
|
|
|
|
|
return InstallProgressView(
|
|
|
|
|
productName: "Adobe Photoshop",
|
|
|
|
|
progress: 1.0,
|
|
|
|
|
status: "安装完成",
|
|
|
|
|
onCancel: {},
|
2025-03-27 01:05:20 +08:00
|
|
|
onRetry: nil,
|
|
|
|
|
errorDetails: nil
|
2024-11-04 17:40:01 +08:00
|
|
|
)
|
|
|
|
|
.environmentObject(networkManager)
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-14 13:30:13 +08:00
|
|
|
#Preview("在深色模式下") {
|
2024-11-04 17:40:01 +08:00
|
|
|
let networkManager = NetworkManager()
|
|
|
|
|
return InstallProgressView(
|
|
|
|
|
productName: "Adobe Photoshop",
|
|
|
|
|
progress: 0.75,
|
|
|
|
|
status: "正在安装...",
|
|
|
|
|
onCancel: {},
|
2025-03-27 01:05:20 +08:00
|
|
|
onRetry: nil,
|
|
|
|
|
errorDetails: nil
|
2024-11-04 17:40:01 +08:00
|
|
|
)
|
|
|
|
|
.environmentObject(networkManager)
|
|
|
|
|
.preferredColorScheme(.dark)
|
|
|
|
|
}
|