// // SettingsNetworkView 2.swift // punchnet // // Created by 安礼成 on 2026/3/19. // import SwiftUI struct SettingsNetworkView: View { @Environment(AppContext.self) var appContext: AppContext @Environment(\.openURL) var openURL @State private var selectedExitNode: SDLAPIClient.NetworkSession.ExitNode? var body: some View { ScrollView(.vertical, showsIndicators: false) { VStack(alignment: .leading, spacing: 24) { // MARK: - 网络部分 sectionHeader(title: "网络配置", icon: "network") if let networkSession = appContext.networkSession { VStack(spacing: 16) { HStack { VStack(alignment: .leading, spacing: 4) { Text("网络") .font(.subheadline) .foregroundColor(.secondary) Text(networkSession.networkName) .font(.headline) } Spacer() } Divider() HStack { Button { self.openNetworkUrl(url: networkSession.networkUrl) } label: { Label("进入管理平台", systemImage: "arrow.up.right.square") } .buttonStyle(.plain) .foregroundColor(.blue) Spacer() Button("查看详情") { self.openNetworkUrl(url: networkSession.networkUrl) } .buttonStyle(.bordered) .controlSize(.small) } } .padding(16) .background(Color.primary.opacity(0.03)) .cornerRadius(12) // 出口节点项 HStack { VStack(alignment: .leading, spacing: 4) { Text("出口节点") .font(.caption) .foregroundColor(.secondary) if let selectedExitNode = self.selectedExitNode { Text(selectedExitNode.nodeName) .font(.system(size: 15, weight: .medium)) } } Spacer() Menu { ForEach(networkSession.exitNodes, id: \.uuid) { node in Button { self.selectedExitNode = node } label: { Text(node.nodeName) } } } label: { Text("更改") .font(.subheadline) .padding(.horizontal, 10) .padding(.vertical, 4) .background(Capsule().fill(Color.blue.opacity(0.1))) .foregroundColor(.blue) } .buttonStyle(.plain) } .padding(16) .background(Color.primary.opacity(0.03)) .cornerRadius(12) } // MARK: - 授权与安全 sectionHeader(title: "授权状态", icon: "checkmark.shield.fill") VStack(spacing: 0) { StatusRow(title: "当前状态", value: "有效", valueColor: .green) Divider() .padding(.leading, 16) StatusRow(title: "有效期", value: "临时 (至断开连接)", valueColor: .secondary) } .background(Color.primary.opacity(0.03)) .cornerRadius(12) } .padding(32) .frame(maxWidth: 600, alignment: .leading) } .onAppear { self.selectedExitNode = self.appContext.networkSession?.exitNodes.first } } // MARK: - 辅助组件 private func openNetworkUrl(url: String) { if let url = URL(string: url) { openURL(url) { accepted in if accepted { print("浏览器已成功打开") } else { print("打开失败(可能是 URL 格式错误)") } } } } // 辅助头部组件 private func sectionHeader(title: String, icon: String) -> some View { HStack { Image(systemName: icon) .foregroundColor(.blue) Text(title) .font(.system(size: 16, weight: .bold)) } .padding(.leading, 4) } } struct StatusRow: View { let title: String let value: String let valueColor: Color var body: some View { HStack { Text(title) .font(.system(size: 14)) .foregroundColor(.primary.opacity(0.8)) Spacer() Text(value) .font(.system(size: 14, weight: .medium)) .foregroundColor(valueColor) } .padding(16) } }