155 lines
5.4 KiB
Swift
155 lines
5.4 KiB
Swift
import SwiftUI
|
|
|
|
struct SettingsView: View {
|
|
@StateObject private var model: SettingsViewModel
|
|
@State private var showsClearDialog = false
|
|
|
|
init(model: SettingsViewModel? = nil) {
|
|
_model = StateObject(wrappedValue: model ?? DefaultContainer.makeSettingsModel())
|
|
}
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
AITheme.canvas.ignoresSafeArea()
|
|
ScrollView {
|
|
LazyVStack(spacing: 12) {
|
|
if let errorMessage = model.errorMessage {
|
|
AICard {
|
|
Text(errorMessage)
|
|
.font(.subheadline)
|
|
.foregroundStyle(AITheme.danger)
|
|
}
|
|
}
|
|
if let notice = model.notice {
|
|
AICard {
|
|
Text(notice)
|
|
.font(.subheadline)
|
|
.foregroundStyle(AITheme.success)
|
|
}
|
|
}
|
|
|
|
runtimeCard
|
|
storageCard
|
|
securityCard
|
|
backgroundCard
|
|
}
|
|
.padding(16)
|
|
}
|
|
}
|
|
.navigationTitle("设置")
|
|
.toolbar {
|
|
ToolbarItemGroup(placement: .primaryAction) {
|
|
Button {
|
|
Task { await model.refresh() }
|
|
} label: {
|
|
Label("刷新", systemImage: "arrow.clockwise")
|
|
}
|
|
.buttonStyle(AIButtonStyle(kind: .secondary, size: .regular))
|
|
.disabled(model.isLoading)
|
|
}
|
|
}
|
|
.refreshable {
|
|
await model.refresh()
|
|
}
|
|
.confirmationDialog(
|
|
"确认清除运行日志?此操作不可恢复。",
|
|
isPresented: $showsClearDialog,
|
|
titleVisibility: .visible
|
|
) {
|
|
Button("清除运行日志", role: .destructive) {
|
|
Task { await model.clearRuntimeLogs() }
|
|
}
|
|
Button("取消", role: .cancel) {}
|
|
}
|
|
.task {
|
|
await model.refresh()
|
|
}
|
|
}
|
|
|
|
private var runtimeCard: some View {
|
|
AICard {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
sectionTitle("应用信息", systemImage: "info.circle")
|
|
if let runtime = model.runtime {
|
|
infoRow("名称", runtime.appName)
|
|
infoRow("版本", "\(runtime.marketingVersion) (\(runtime.buildVersion))")
|
|
infoRow("标识符", AppMetadata.bundleIdentifier)
|
|
infoRow("设备", "\(runtime.platform) · \(runtime.deviceModel)")
|
|
} else {
|
|
Text("正在读取应用信息…")
|
|
.font(.subheadline)
|
|
.foregroundStyle(AITheme.muted)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private var storageCard: some View {
|
|
AICard {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
sectionTitle("本地数据", systemImage: "externaldrive")
|
|
infoRow("实例配置", "App Documents/instances.json")
|
|
infoRow("日志中心", "App Documents/log-center.json")
|
|
infoRow("日志数量", model.storageLabel)
|
|
Button {
|
|
showsClearDialog = true
|
|
} label: {
|
|
Label("清除运行日志", systemImage: "trash")
|
|
}
|
|
.buttonStyle(AIButtonStyle(kind: .danger, size: .regular))
|
|
}
|
|
}
|
|
}
|
|
|
|
private var securityCard: some View {
|
|
AICard {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
sectionTitle("安全与登录", systemImage: "lock.shield")
|
|
infoRow("实例密码", "保存在 iOS 钥匙串")
|
|
infoRow("登录会话", "仅保存在内存")
|
|
infoRow("访问密码", "已停用")
|
|
}
|
|
}
|
|
}
|
|
|
|
private var backgroundCard: some View {
|
|
AICard {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
sectionTitle("后台能力", systemImage: "clock.badge.exclamationmark")
|
|
Text(model.serverCapabilitiesNotice)
|
|
.font(.subheadline)
|
|
.foregroundStyle(AITheme.muted)
|
|
if model.serverCapabilities.isEmpty {
|
|
AITag("无后台任务", tint: AITheme.muted)
|
|
} else {
|
|
ForEach(model.serverCapabilities) { capability in
|
|
infoRow(capability.name, capability.detail)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func sectionTitle(_ title: String, systemImage: String) -> some View {
|
|
HStack(spacing: 8) {
|
|
Image(systemName: systemImage)
|
|
.foregroundStyle(AITheme.primary)
|
|
Text(title)
|
|
.font(.headline.weight(.bold))
|
|
Spacer()
|
|
}
|
|
}
|
|
|
|
private func infoRow(_ title: String, _ value: String) -> some View {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(title)
|
|
.font(.caption)
|
|
.foregroundStyle(AITheme.muted)
|
|
Text(value)
|
|
.font(.subheadline)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.textSelection(.enabled)
|
|
}
|
|
}
|
|
}
|