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) } } }