import SwiftUI struct SettingsView: View { @StateObject private var model: SettingsViewModel @ObservedObject private var themeStore = ThemeStore.shared @State private var showsClearDialog = false init(model: SettingsViewModel? = nil) { _model = StateObject(wrappedValue: model ?? DefaultContainer.makeSettingsModel()) } var body: some View { ScrollView { LazyVStack(spacing: 12) { if let errorMessage = model.errorMessage { AIStatusBanner( errorMessage, tint: AITheme.danger, systemImage: "exclamationmark.triangle") } if let notice = model.notice { AIStatusBanner(notice, tint: AITheme.success, systemImage: "checkmark.circle") } runtimeCard themeCard storageCard securityCard dangerZoneCard } .padding(16) .padding(.bottom, 8) } .background(AITheme.canvas.ignoresSafeArea()) .navigationTitle("设置") .toolbar { ToolbarItemGroup(placement: .primaryAction) { AIToolbarButton( "刷新", systemImage: "arrow.clockwise", kind: .primary ) { Task { await model.refresh() } } .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) { AISectionTitle("应用信息", systemImage: "info.circle") if let runtime = model.runtime { AIKeyValueRow("版本", value: "\(runtime.marketingVersion) (\(runtime.buildVersion))") AIKeyValueRow("设备", value: runtime.deviceModel) } else { Text("正在读取应用信息…") .font(.subheadline) .foregroundStyle(AITheme.muted) } } } } private var themeCard: some View { AICard { VStack(alignment: .leading, spacing: 12) { AISectionTitle("外观", systemImage: "circle.lefthalf.filled", detail: themeStore.mode.label) AISegmentedControl( "主题模式", selection: Binding( get: { themeStore.mode }, set: { themeStore.set($0) } ), options: ThemeMode.allCases.map { (value: $0, title: $0.label) } ) } } } private var storageCard: some View { AICard { NavigationLink { LogsView() } label: { VStack(alignment: .leading, spacing: 12) { HStack(spacing: 10) { AISectionTitle("本地数据", systemImage: "externaldrive") .frame(maxWidth: .infinity, alignment: .leading) Image(systemName: "chevron.right") .font(.footnote.weight(.semibold)) .foregroundStyle(AITheme.muted) .accessibilityHidden(true) } AIKeyValueRow("数据位置", value: "仅保存在本机设备") AIKeyValueRow("当前占用", value: model.storageLabel) } } .buttonStyle(.plain) .accessibilityHint("进入日志与诊断") } } private var securityCard: some View { AICard { VStack(alignment: .leading, spacing: 12) { AISectionTitle("隐私与数据", systemImage: "lock.shield") AIKeyValueRow("节点信息", value: "仅保存在本机设备") AIKeyValueRow("实例密码", value: "保存在本地数据文件中") AIKeyValueRow("登录会话", value: "应用关闭后自动清除") AIStatusBanner( "节点地址、实例密码和运行数据都保存在本机。应用只连接你添加的节点,登录会话关闭应用后即被清除。", tint: AITheme.primary, systemImage: "shield.checkered" ) } } } private var dangerZoneCard: some View { AICard { VStack(alignment: .leading, spacing: 12) { AISectionTitle("数据清理", systemImage: "trash.square", detail: "不可恢复") Button { showsClearDialog = true } label: { Label("清除运行日志", systemImage: "trash") } .buttonStyle(AIButtonStyle(kind: .danger, size: .regular, fillsWidth: true)) } } } }