// // SettingsAboutView.swift // punchnet // // Created by 安礼成 on 2026/1/19. // import SwiftUI struct SettingsAboutView: View { @Environment(\.openURL) private var openURL @State private var isShowingFeedbackSheet = false @State private var appPoliciesInfo: SDLAPIClient.AppPoliciesInfo? // 检查更新逻辑 @State private var updateManager = AppUpdateManager.shared @State private var showNoUpdateAlert = false @State private var manualUpdateInfo: SDLAPIClient.AppUpgradeInfo? var body: some View { ScrollView(.vertical, showsIndicators: false) { VStack(alignment: .leading, spacing: 32) { // MARK: - 品牌展示区 (左对齐) HStack(spacing: 20) { // App Icon 保持精致的圆角和阴影 ZStack { RoundedRectangle(cornerRadius: 16, style: .continuous) .fill(Color.blue.gradient) .frame(width: 64, height: 64) .shadow(color: .blue.opacity(0.2), radius: 8, x: 0, y: 4) Image(systemName: "bolt.shield.fill") .font(.system(size: 32)) .foregroundColor(.white) } VStack(alignment: .leading, spacing: 4) { Text("PunchNet") .font(.system(size: 22, weight: .bold)) Text("版本 \(SystemConfig.version_name)") .font(.subheadline) .foregroundColor(.secondary) Text(SystemConfig.systemInfo) .font(.caption2) .padding(.horizontal, 6) .padding(.vertical, 2) .background(Color.primary.opacity(0.05)) .cornerRadius(4) } } .padding(.top, 10) // MARK: - 核心操作卡片 VStack(alignment: .leading, spacing: 0) { AboutRow(title: "检查更新", icon: "arrow.clockwise.circle") { Button("立即检查") { // 检查更新逻辑 Task {@MainActor in await self.checkAppUpgrade() } } .buttonStyle(.plain) .foregroundColor(.blue) .font(.subheadline.bold()) .disabled(updateManager.isChecking) } Divider().padding(.leading, 44) AboutRow(title: "用户反馈", icon: "bubble.left.and.exclamationmark.bubble.right") { Image(systemName: "chevron.right") .font(.caption) .foregroundColor(.secondary) } .onTapGesture { self.isShowingFeedbackSheet = true } } .background(Color.primary.opacity(0.03)) .cornerRadius(12) .overlay(RoundedRectangle(cornerRadius: 12).stroke(Color.primary.opacity(0.05), lineWidth: 1)) // MARK: - 法律文档卡片 VStack(alignment: .leading, spacing: 0) { AboutRow(title: "隐私政策", icon: "doc.text.magnifyingglass") { Image(systemName: "arrow.up.right") .font(.caption) .foregroundColor(.secondary) } .onTapGesture { if let privacyPolicyUrl = self.appPoliciesInfo?.privacyPolicyUrl, let privacyUrl = URL(string: privacyPolicyUrl) { openURL(privacyUrl) } } Divider().padding(.leading, 44) AboutRow(title: "服务条款", icon: "scroll") { Image(systemName: "arrow.up.right") .font(.caption) .foregroundColor(.secondary) } .onTapGesture { if let termsOfServiceUrl = self.appPoliciesInfo?.termsOfServiceUrl, let termsUrl = URL(string: termsOfServiceUrl) { openURL(termsUrl) } } } .background(Color.primary.opacity(0.03)) .cornerRadius(12) // MARK: - 底部版权 VStack(alignment: .leading, spacing: 4) { Text("© 2024-2026 PunchNet Inc.") Text("保留所有权利。") } .font(.caption2) .foregroundColor(.secondary) .padding(.leading, 4) } .padding(32) .frame(maxWidth: 600, alignment: .leading) } .sheet(isPresented: $isShowingFeedbackSheet) { VStack { HStack { Spacer() Button { isShowingFeedbackSheet = false } label: { Text("关闭") } .buttonStyle(.plain) .padding() } // 复用你写的反馈视图 SettingsUserIssueView() } .frame(width: 500, height: 600) // 设置弹窗大小 } // 手动检查的 Sheet 弹出 .sheet(item: $manualUpdateInfo) { info in AppUpdateView(info: info) { self.manualUpdateInfo = nil } } .alert(isPresented: $showNoUpdateAlert) { Alert(title: Text("检查更新"), message: Text("您当前使用的是最新版本。")) } .task { self.appPoliciesInfo = try? await SDLAPIClient.appPolicies() _ = try? await SDLAPIClient.appCheckUpdate() } } private func checkAppUpgrade() async { let hasUpdate = await updateManager.checkUpdate(isManual: true) if hasUpdate { // 手动检查发现更新,赋值给 sheet 绑定的变量 self.manualUpdateInfo = updateManager.updateInfo } else { self.showNoUpdateAlert = true } } } // MARK: - 复用之前的行组件 struct AboutRow: View { let title: String let icon: String let trailingContent: () -> Content var body: some View { HStack(spacing: 12) { Image(systemName: icon) .foregroundColor(.blue) .font(.system(size: 14)) .frame(width: 20) Text(title) .font(.system(size: 14, weight: .medium)) Spacer() trailingContent() } .padding(.horizontal, 16) .padding(.vertical, 14) .contentShape(Rectangle()) } } #Preview { SettingsAboutView() }