diff --git a/punchnet/Views/Network/NetworkView.swift b/punchnet/Views/Network/NetworkView.swift index 2a8ab9d..011e18c 100644 --- a/punchnet/Views/Network/NetworkView.swift +++ b/punchnet/Views/Network/NetworkView.swift @@ -6,7 +6,15 @@ import Observation // MARK: - 基础模型协议 enum ConnectState { - case waitAuth, connected, disconnected + case waitAuth + case connected + case disconnected +} + +// 资源展示模式 +enum NetworkShowMode: String, CaseIterable { + case resource = "访问资源" + case device = "成员设备" } // MARK: - 主网络视图 @@ -15,21 +23,67 @@ struct NetworkView: View { @Environment(\.openWindow) private var openWindow @State private var networkModel = NetworkModel() - @State private var showMode: ShowMode = .resource + @State private var showMode: NetworkShowMode = .resource @State private var connectState: ConnectState = .disconnected @State private var isConnecting: Bool = false private var vpnManager = VPNManager.shared - - enum ShowMode: String, CaseIterable { - case resource = "访问资源" - case device = "成员设备" - } var body: some View { VStack(spacing: 0) { // 1. 头部区域 (Header) - headerSection + HStack(spacing: 16) { + ZStack { + Circle() + .fill(connectState == .connected ? Color.green.opacity(0.15) : Color.primary.opacity(0.05)) + .frame(width: 36, height: 36) + + Image(systemName: connectState == .connected ? "checkmark.shield.fill" : "shield.slash.fill") + .symbolRenderingMode(.hierarchical) + .foregroundStyle(connectState == .connected ? Color.green : Color.secondary) + .font(.system(size: 16)) + } + + VStack(alignment: .leading, spacing: 2) { + Text(appContext.networkSession?.networkName ?? "未连接网络") + .font(.system(size: 14, weight: .semibold)) + + if connectState == .connected { + Text("虚拟局域网 IP: \(networkModel.networkContext.ip)") + .font(.system(size: 11, design: .monospaced)) + .foregroundColor(.secondary) + } else { + Text("PunchNet 服务未就绪") + .font(.caption) + .foregroundColor(.secondary) + } + } + + Spacer() + + if connectState == .connected { + Picker("", selection: $showMode) { + ForEach(NetworkShowMode.allCases, id: \.self) { + Text($0.rawValue).tag($0) + } + } + .pickerStyle(.segmented) + .frame(width: 160) + } + + Button { + openWindow(id: "settings") + } label: { + Image(systemName: "slider.horizontal.3") + .font(.system(size: 14)) + .foregroundColor(.secondary) + } + .buttonStyle(.plain) + .help("配置中心") + } + .padding(.horizontal, 20) + .padding(.vertical, 14) + .background(VisualEffectView(material: .headerView, blendingMode: .withinWindow)) Divider() @@ -39,9 +93,52 @@ struct NetworkView: View { case .waitAuth: NetworkWaitAuthView(networkModel: networkModel) case .connected: - connectedContent + if showMode == .resource { + // 资源视图:网格布局 + ScrollView { + LazyVGrid(columns: [ + GridItem(.flexible(), spacing: 8), + GridItem(.flexible(), spacing: 8), + GridItem(.flexible(), spacing: 8) + ], spacing: 10) { + ForEach(networkModel.networkContext.resourceList, id: \.uuid) { res in + ResourceItemCard(resource: res) + } + } + .padding(20) + } + .transition(.opacity) + .frame(maxWidth: .infinity) + } else { + // 设备视图:双栏布局 + NetworkDeviceGroupView(networkModel: networkModel) + .transition(.asymmetric(insertion: .move(edge: .trailing), removal: .opacity)) + } case .disconnected: - disconnectedContent + VStack(spacing: 20) { + Spacer() + Image(systemName: "antenna.radiowaves.left.and.right") + .font(.system(size: 40, weight: .ultraLight)) + .foregroundStyle(.tertiary) + .symbolEffect(.pulse, options: .repeating) + + Text("尚未接入网络") + .font(.headline) + + Button(action: { startConnection() }) { + if isConnecting { + ProgressView() + .controlSize(.small) + .frame(width: 80) + } else { + Text("建立安全连接") + .frame(width: 80) + } + } + .buttonStyle(.borderedProminent) + .disabled(isConnecting) + Spacer() + } } } .frame(maxWidth: .infinity, maxHeight: .infinity) @@ -57,121 +154,6 @@ struct NetworkView: View { } } } -} - -// MARK: - 视图组件扩展 -extension NetworkView { - - private var headerSection: some View { - HStack(spacing: 16) { - statusIndicator - - VStack(alignment: .leading, spacing: 2) { - Text(appContext.networkSession?.networkName ?? "未连接网络") - .font(.system(size: 14, weight: .semibold)) - - if connectState == .connected { - Text("虚拟局域网 IP: \(networkModel.networkContext.ip)") - .font(.system(size: 11, design: .monospaced)) - .foregroundColor(.secondary) - } else { - Text("PunchNet 服务未就绪") - .font(.caption) - .foregroundColor(.secondary) - } - } - - Spacer() - - if connectState == .connected { - Picker("", selection: $showMode) { - ForEach(ShowMode.allCases, id: \.self) { - Text($0.rawValue).tag($0) - } - } - .pickerStyle(.segmented) - .frame(width: 160) - } - - Button { - openWindow(id: "settings") - } label: { - Image(systemName: "slider.horizontal.3") - .font(.system(size: 14)) - .foregroundColor(.secondary) - } - .buttonStyle(.plain) - .help("配置中心") - } - .padding(.horizontal, 20) - .padding(.vertical, 14) - .background(VisualEffectView(material: .headerView, blendingMode: .withinWindow)) - } - - private var statusIndicator: some View { - ZStack { - Circle() - .fill(connectState == .connected ? Color.green.opacity(0.15) : Color.primary.opacity(0.05)) - .frame(width: 36, height: 36) - - Image(systemName: connectState == .connected ? "checkmark.shield.fill" : "shield.slash.fill") - .symbolRenderingMode(.hierarchical) - .foregroundStyle(connectState == .connected ? Color.green : Color.secondary) - .font(.system(size: 16)) - } - } - - @ViewBuilder - private var connectedContent: some View { - if showMode == .resource { - // 资源视图:网格布局 - ScrollView { - LazyVGrid(columns: [ - GridItem(.flexible(), spacing: 8), - GridItem(.flexible(), spacing: 8), - GridItem(.flexible(), spacing: 8) - ], spacing: 10) { - ForEach(networkModel.networkContext.resourceList, id: \.uuid) { res in - ResourceItemCard(resource: res) - } - } - .padding(20) - } - .transition(.opacity) - .frame(maxWidth: .infinity) - } else { - // 设备视图:双栏布局 - NetworkDeviceGroupView(networkModel: networkModel) - .transition(.asymmetric(insertion: .move(edge: .trailing), removal: .opacity)) - } - } - - private var disconnectedContent: some View { - VStack(spacing: 20) { - Spacer() - Image(systemName: "antenna.radiowaves.left.and.right") - .font(.system(size: 40, weight: .ultraLight)) - .foregroundStyle(.tertiary) - .symbolEffect(.pulse, options: .repeating) - - Text("尚未接入网络") - .font(.headline) - - Button(action: { startConnection() }) { - if isConnecting { - ProgressView() - .controlSize(.small) - .frame(width: 80) - } else { - Text("建立安全连接") - .frame(width: 80) - } - } - .buttonStyle(.borderedProminent) - .disabled(isConnecting) - Spacer() - } - } private func syncState(_ status: VPNManager.VPNStatus) { switch status { diff --git a/punchnet/punchnetApp.swift b/punchnet/punchnetApp.swift index cbf8082..6309689 100644 --- a/punchnet/punchnetApp.swift +++ b/punchnet/punchnetApp.swift @@ -64,7 +64,7 @@ struct punchnetApp: App { // } // } // } - .windowResizability(.contentSize) + //.windowResizability(.contentSize) .windowToolbarStyle(.unified) .defaultPosition(.center)