diff --git a/punchnet/Views/AppContext.swift b/punchnet/Views/AppContext.swift index 5f7cd1e..b2b1e8b 100644 --- a/punchnet/Views/AppContext.swift +++ b/punchnet/Views/AppContext.swift @@ -12,6 +12,9 @@ import Observation class AppContext { var noticePort: Int + // 调用 "/connect" 之后的网络信息 + var networkContext: NetworkContext? + init(noticePort: Int) { self.noticePort = noticePort } diff --git a/punchnet/Views/Network/NetworkModel.swift b/punchnet/Views/Network/NetworkModel.swift index 8afc547..444c415 100644 --- a/punchnet/Views/Network/NetworkModel.swift +++ b/punchnet/Views/Network/NetworkModel.swift @@ -53,6 +53,7 @@ struct Node: Codable { struct NetworkContext: Codable { let ip: String let maskLen: UInt8 + // 主机名称 let hostname: String let identityId: UInt32 let resourceList: [Resource] @@ -110,13 +111,15 @@ class NetworkModel { } } - func connect(networkSession: UserContext.NetworkSession) async throws { + func connect(networkSession: UserContext.NetworkSession) async throws -> NetworkContext { let params: [String: Any] = [ "client_id": SystemConfig.getClientId(), "access_token": networkSession.accessToken ] self.networkContext = try await SDLAPIClient.doPost(path: "/connect", params: params, as: NetworkContext.self) + + return self.networkContext } } diff --git a/punchnet/Views/Network/NetworkView.swift b/punchnet/Views/Network/NetworkView.swift index d036862..c31f4f3 100644 --- a/punchnet/Views/Network/NetworkView.swift +++ b/punchnet/Views/Network/NetworkView.swift @@ -190,9 +190,11 @@ extension NetworkView { return } - try await networkModel.connect(networkSession: session) + let context = try await networkModel.connect(networkSession: session) + + // 登陆后需要保存到app的上线文 + self.appContext.networkContext = context - let context = networkModel.networkContext if let options = SystemConfig.getOptions( networkId: UInt32(session.networkId), networkDomain: session.networkDomain, diff --git a/punchnet/Views/Settings/SettingsDeviceView.swift b/punchnet/Views/Settings/SettingsDeviceView.swift index 8a2cc87..cbf1088 100644 --- a/punchnet/Views/Settings/SettingsDeviceView.swift +++ b/punchnet/Views/Settings/SettingsDeviceView.swift @@ -8,36 +8,115 @@ import SwiftUI struct SettingsDeviceView: View { + @Environment(AppContext.self) var appContext: AppContext + var body: some View { - VStack(alignment: .leading) { - Text("设备") - - HStack { - Text("设备名称") - Text("史蒂夫的air") + ScrollView(.vertical, showsIndicators: false) { + VStack(alignment: .leading, spacing: 28) { - Button { + // MARK: - 设备概览标题 + HStack(spacing: 16) { + Image(systemName: "laptopcomputer") + .font(.system(size: 36)) + .foregroundStyle(.blue.gradient) + .frame(width: 60, height: 60) + .background(Color.blue.opacity(0.1)) + .cornerRadius(12) - } label: { - Text("修改") + VStack(alignment: .leading, spacing: 4) { + Text(self.appContext.networkContext?.hostname ?? "为定义") + .font(.title3.bold()) + + Text(SystemConfig.systemInfo) + .font(.subheadline) + .foregroundColor(.secondary) + } } + .padding(.horizontal, 4) + + // MARK: - 详细参数卡片 + VStack(alignment: .leading, spacing: 0) { + // 设备名称行 + DevicePropertyRow(title: "设备名称", value: self.appContext.networkContext?.hostname ?? "为定义") { + Button { + // 修改逻辑 + } label: { + Text("修改") + .font(.subheadline.bold()) + .padding(.horizontal, 12) + .padding(.vertical, 4) + .background(Capsule().fill(Color.blue.opacity(0.1))) + .foregroundColor(.blue) + } + .buttonStyle(.plain) + } + + Divider().padding(.leading, 16) + + // IPv4 行 + DevicePropertyRow(title: "虚拟 IPv4", value: self.appContext.networkContext?.ip ?? "0.0.0.0") { + Image(systemName: "info.circle") + .foregroundColor(.secondary) + } + + Divider().padding(.leading, 16) + +// // IPv6 行 +// DevicePropertyRow(title: "虚拟 IPv6", value: "fe80::ab:ef:1") { +// Text("已加密") +// .font(.caption2.bold()) +// .padding(.horizontal, 6) +// .padding(.vertical, 2) +// .background(Color.green.opacity(0.1)) +// .foregroundColor(.green) +// .cornerRadius(4) +// } + } + .background(Color.primary.opacity(0.03)) + .cornerRadius(12) + .overlay(RoundedRectangle(cornerRadius: 12).stroke(Color.primary.opacity(0.05), lineWidth: 1)) + + // MARK: - 底部说明 + Text("此设备在虚拟网络中是唯一的,修改名称不会影响连接标识。") + .font(.caption) + .foregroundColor(.secondary) + .padding(.horizontal, 4) + + Spacer() } - - HStack { - Text("虚拟IPv4") - Text("192.168.1.1") - } - - HStack { - Text("虚拟IPv6") - Text("ab:ef:1") - } - - Spacer() + .padding(32) + .frame(maxWidth: 600, alignment: .leading) } } } -#Preview { - SettingsDeviceView() +// MARK: - 子组件:设备属性行 +struct DevicePropertyRow: View { + let title: String + let value: String + let trailingContent: () -> Content + + init(title: String, value: String, @ViewBuilder trailingContent: @escaping () -> Content) { + self.title = title + self.value = value + self.trailingContent = trailingContent + } + + var body: some View { + HStack { + Text(title) + .foregroundColor(.secondary) + .frame(width: 100, alignment: .leading) + + Text(value) + .fontWeight(.medium) + .font(.system(.body, design: .monospaced)) // 使用等宽字体显示 IP 更专业 + + Spacer() + + trailingContent() + } + .padding(.horizontal, 16) + .padding(.vertical, 14) + } } diff --git a/punchnet/punchnetApp.swift b/punchnet/punchnetApp.swift index e615bec..39bf3b5 100644 --- a/punchnet/punchnetApp.swift +++ b/punchnet/punchnetApp.swift @@ -50,8 +50,7 @@ struct punchnetApp: App { var body: some Scene { WindowGroup(id: "main") { - // RootView() - SettingsView() + RootView() .navigationTitle("") .environment(self.appContext) .environment(self.userContext) @@ -72,6 +71,7 @@ struct punchnetApp: App { Window("设置", id: "settings") { SettingsView() .environment(self.userContext) + .environment(self.appContext) } .defaultSize(width: 800, height: 500) .defaultPosition(.center) @@ -79,6 +79,7 @@ struct punchnetApp: App { Window("重置密码", id: "resetPassword") { ResetPasswordRootView() .environment(self.userContext) + .environment(self.appContext) } .defaultSize(width: 800, height: 500) .defaultPosition(.center) @@ -86,6 +87,7 @@ struct punchnetApp: App { Window("注册", id: "register") { ResetPasswordRootView() .environment(self.userContext) + .environment(self.appContext) } .defaultSize(width: 800, height: 500) .defaultPosition(.center)