// // NetworkView.swift // punchnet // // Created by 安礼成 on 2026/1/16. // import SwiftUI struct NetworkView: View { @Environment(UserContext.self) var userContext: UserContext @State private var networkModel = NetworkModel() @State private var showMode: ShowMode = .resource // 展示状态 enum ShowMode { case resource case device } var body: some View { VStack { HStack { VStack { HStack(alignment: .center) { Text(userContext.networkSession?.networkName ?? "未知") Text(">") Spacer() } HStack { Toggle("", isOn: $networkModel.isOn) .toggleStyle(SwitchToggleStyle(tint: .green)) .disabled(true) Text("已连接") Spacer() } } .frame(width: 320) // 显示设备和资源选项 HStack { Button { self.showMode = .resource } label: { Text("资源") } Button { self.showMode = .device } label: { Text("设备") } } Spacer() } Group { switch self.networkModel.connectState { case .waitAuth: NetworkWaitAuthView(networkModel: self.networkModel) case .connected: Group { switch self.showMode { case .resource: NetworkResourceGroupView(networkModel: self.networkModel) case .device: NetworkDeviceGroupView(networkModel: self.networkModel) } } case .disconnected: NetworkDisconnctedView(networkModel: self.networkModel) } } Spacer() } .padding(.top, 10) .padding(.leading, 10) .toolbar { ToolbarItem(placement: .primaryAction) { Button { print("clicked") } label: { Image(systemName: "gearshape") } } } } } // 网络处于未连接状态 struct NetworkDisconnctedView: View { @Bindable var networkModel: NetworkModel @Environment(UserContext.self) var userContext: UserContext @State private var showAlert = false @State private var errorMessage = "" var body: some View { ZStack { Color.clear VStack { Button { Task { @MainActor in do { try await self.connect() try await self.startVpn() } catch let err { self.showAlert = true self.errorMessage = err.localizedDescription } } } label: { Text("连接") .font(.system(size: 14, weight: .regular)) .padding([.top, .bottom], 8) .padding([.leading, .trailing], 30) .foregroundColor(.white) } .background(Color(red: 74/255, green: 207/255, blue: 154/255)) .cornerRadius(5) .frame(width: 120, height: 35) Button { Task { try await VPNManager.shared.disableVpn() } } label: { Text("关闭") .font(.system(size: 14, weight: .regular)) .padding([.top, .bottom], 8) .padding([.leading, .trailing], 30) .foregroundColor(.white) } .background(Color(red: 74/255, green: 207/255, blue: 154/255)) .cornerRadius(5) .frame(width: 120, height: 35) } } .alert(isPresented: $showAlert) { Alert(title: Text("提示"), message: Text(self.errorMessage)) } } private func connect() async throws { guard let networkSession = userContext.networkSession else { return } try await networkModel.connect(networkSession: networkSession) } // 执行登陆操作 private func startVpn() async throws { let clientId = SystemConfig.getClientId() let options = SystemConfig.getOptions(networkId: 8, networkDomain: "punchnet.com", ip: "10.211.179.1", maskLen: 24, accessToken: "accessToken1234", identityId: 1234, hostname: "mysql", noticePort: 1234) // token存在则优先使用token try await VPNManager.shared.enableVpn(options: options!) } } // 网络处于连接状态 // 显示资源信息 struct NetworkResourceGroupView: View { @Bindable var networkModel: NetworkModel var body: some View { LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 3), spacing: 8) { ForEach(self.networkModel.resourceList, id: \.id) { resource in NetworkResourceView(resource: resource) } } } } struct NetworkResourceView: View { var resource: NetworkModel.Resource var body: some View { VStack { HStack { Text(resource.connectionStatus) Text(resource.name) .font(.system(size: 14, weight: .regular)) } Text(resource.url) .font(.system(size: 14, weight: .regular)) .padding(.leading, 30) } } } // 显示设备信息 struct NetworkDeviceGroupView: View { @Bindable var networkModel: NetworkModel @State private var selectedId: Int? var body: some View { NavigationSplitView { List(self.networkModel.nodeList, id: \.id, selection: $selectedId) { node in NetworkNodeHeadView(node: node) } .listStyle(.sidebar) .onChange(of: selectedId) { self.networkModel.changeSelectedNode(nodeId: selectedId) } .onAppear { if selectedId == nil { selectedId = self.networkModel.nodeList.first?.id } } } detail: { NetworkNodeDetailView(node: $networkModel.selectedNode) } } } struct NetworkNodeHeadView: View { var node: NetworkModel.Node var body: some View { VStack { HStack { Text(node.connectStatus == 1 ? "yes" : "no") Text(node.name) .font(.system(size: 14, weight: .regular)) } Text(node.ip) .font(.system(size: 14, weight: .regular)) .padding(.leading, 30) } } } struct NetworkNodeDetailView: View { @Binding var node: NetworkModel.Node? var body: some View { Group { if let node { List { Section { HStack { Text("连接状态") Text("\(node.connectStatus)") Spacer() } HStack { Text("虚拟IPv4") Text("\(node.ip)") Spacer() } HStack { Text("操作系统") Text("\(node.system)") Spacer() } } // Section("服务列表") { // ForEach(device.resources, id: \.id) { resource in // HStack { // Text("\(resource.name)") // Text("\(resource.schema)") // } // } // } } } else { EmptyView() } } } } struct NetworkWaitAuthView: View { @Bindable var networkModel: NetworkModel var body: some View { Color.clear .overlay { Text("等待确认中") } } } #Preview { NetworkView() }