import SwiftUI // MARK: - SettingsView /// The settings screen with storage info, debug toggles, /// background keep-alive guide, and app branding. /// Redesigned to use a minimalist wireframe business style. struct SettingsView: View { @State private var showClearCacheAlert = false // Mock storage values private let usedStorage: Double = 1.2 private let totalStorage: Double = 5.0 var body: some View { NavigationStack { ZStack { Color.spaceBlack.ignoresSafeArea() ScrollView { VStack(spacing: 20) { // Section: Storage storageSection .padding(.top, 12) // Section: Debug debugSection // Section: Keep-Alive Guide keepAliveSection // Section: About aboutSection // Footer footer .padding(.top, 12) .padding(.bottom, 40) } .padding(.horizontal, 16) } } .navigationTitle("设置") .navigationBarTitleDisplayMode(.inline) .alert("清理缓存", isPresented: $showClearCacheAlert) { Button("取消", role: .cancel) { } Button("清理", role: .destructive) { HapticManager.trigger(.tapFeedback) } } message: { Text("将清除所有本地缓存数据。此操作不可撤销。") } } } // MARK: - Storage Section private var storageSection: some View { settingsCard { VStack(alignment: .leading, spacing: 16) { sectionHeader(icon: "internaldrive", title: "存储空间") // Thin storage bar VStack(alignment: .leading, spacing: 8) { GeometryReader { geometry in ZStack(alignment: .leading) { // Background bar RoundedRectangle(cornerRadius: 3) .fill(Color.primary.opacity(0.06)) .frame(height: 5) // Used portion RoundedRectangle(cornerRadius: 3) .fill(Color.primary) .frame( width: geometry.size.width * (usedStorage / totalStorage), height: 5 ) } } .frame(height: 5) HStack { Text(String(format: "已使用 %.1f GB", usedStorage)) .font(.system(size: 11, weight: .regular)) .foregroundStyle(Color.primary.opacity(0.8)) Spacer() Text(String(format: "总容量 %.0f GB", totalStorage)) .font(.system(size: 11, weight: .regular)) .foregroundStyle(Color.secondary) } } // Clear cache button (minimal red outline) Button { showClearCacheAlert = true } label: { HStack(spacing: 6) { Image(systemName: "trash") .font(.system(size: 12, weight: .light)) Text("清理缓存") .font(.system(size: 13, weight: .regular)) } .foregroundStyle(Color.recordingRed) .padding(.vertical, 8) .frame(maxWidth: .infinity) .overlay( RoundedRectangle(cornerRadius: 6) .stroke(Color.recordingRed.opacity(0.3), lineWidth: 1) ) } .padding(.top, 4) } } } // MARK: - Debug Section private var debugSection: some View { settingsCard { VStack(alignment: .leading, spacing: 14) { sectionHeader(icon: "hammer", title: "开发调试") // Data Storage Mode VStack(alignment: .leading, spacing: 4) { HStack { Text("数据存储模式") .font(.system(size: 13, weight: .regular)) .foregroundStyle(Color.primary) Spacer() Text("纯本地保存") .font(.system(size: 13, weight: .medium)) .foregroundStyle(Color.secondary) } Text("所有会议记录均保存在设备沙盒 SQLite 中,无网络上传") .font(.system(size: 10)) .foregroundStyle(Color.secondary.opacity(0.6)) } } } } // MARK: - Keep-Alive Section private var keepAliveSection: some View { settingsCard { VStack(alignment: .leading, spacing: 14) { sectionHeader(icon: "shield", title: "后台运行保障") Text("为保障全局不中断录音正常运行,请手动开启以下系统权限:") .font(.system(size: 12)) .foregroundStyle(Color.secondary) VStack(alignment: .leading, spacing: 12) { keepAliveStep( number: 1, title: "开启后台音频", description: "Xcode 开启后台模式 -> 音频与 AirPlay", isChecked: true ) keepAliveStep( number: 2, title: "禁用自动锁屏", description: "系统设置 -> 显示与亮度 -> 自动锁定 -> 永不", isChecked: true ) keepAliveStep( number: 3, title: "允许始终访问定位", description: "系统设置 -> 隐私与安全性 -> 定位服务 -> 现场记录 -> 始终允许", isChecked: false ) } .padding(.top, 4) } } } private func keepAliveStep(number: Int, title: String, description: String, isChecked: Bool) -> some View { HStack(alignment: .top, spacing: 10) { ZStack { Circle() .fill(Color.primary.opacity(isChecked ? 0.05 : 0.01)) .frame(width: 24, height: 24) .businessBorder(cornerRadius: 12) if isChecked { Image(systemName: "checkmark") .font(.system(size: 10, weight: .semibold)) .foregroundStyle(Color.primary) } else { Text("\(number)") .font(.system(size: 10, weight: .medium, design: .monospaced)) .foregroundStyle(Color.secondary) } } VStack(alignment: .leading, spacing: 2) { Text(title) .font(.system(size: 13, weight: .medium)) .foregroundStyle(Color.primary) Text(description) .font(.system(size: 11)) .foregroundStyle(Color.secondary.opacity(0.8)) } } } // MARK: - About Section private var aboutSection: some View { settingsCard { VStack(spacing: 14) { sectionHeader(icon: "info.circle", title: "关于") VStack(spacing: 10) { infoRow(label: "应用名称", value: "现场记录 (Celestia Trace)") infoRow(label: "版本信息", value: "0.1.0 (本地持久化单机版)") infoRow(label: "底层框架", value: "SwiftUI + SwiftData") infoRow(label: "系统支持", value: "iOS 17.0+ (模拟器)") } .padding(.top, 4) } } } private func infoRow(label: String, value: String) -> some View { HStack { Text(label) .font(.system(size: 12)) .foregroundStyle(Color.secondary) Spacer() Text(value) .font(.system(size: 12, weight: .regular, design: .monospaced)) .foregroundStyle(Color.primary) } } // MARK: - Footer private var footer: some View { VStack(spacing: 4) { Text("星痕现场记录") .font(.system(size: 10, weight: .bold, design: .monospaced)) .foregroundStyle(Color.secondary.opacity(0.4)) .tracking(2) Text("极简专业现场记录工作台") .font(.system(size: 9)) .foregroundStyle(Color.secondary.opacity(0.3)) } } // MARK: - Reusable Components private func settingsCard(@ViewBuilder content: () -> Content) -> some View { VStack(alignment: .leading, spacing: 0) { content() } .padding(16) .background(Color.cardBackground.opacity(0.15)) .businessBorder(cornerRadius: 10) } private func sectionHeader(icon: String, title: String) -> some View { HStack(spacing: 8) { Image(systemName: icon) .font(.system(size: 12, weight: .light)) .foregroundStyle(Color.secondary) Text(title) .font(.system(size: 14, weight: .semibold)) .foregroundStyle(Color.primary) } } } // MARK: - Preview #Preview { SettingsView() }