fix network
This commit is contained in:
parent
c8c37954ce
commit
fbbef96aa9
@ -6,7 +6,15 @@ import Observation
|
|||||||
|
|
||||||
// MARK: - 基础模型协议
|
// MARK: - 基础模型协议
|
||||||
enum ConnectState {
|
enum ConnectState {
|
||||||
case waitAuth, connected, disconnected
|
case waitAuth
|
||||||
|
case connected
|
||||||
|
case disconnected
|
||||||
|
}
|
||||||
|
|
||||||
|
// 资源展示模式
|
||||||
|
enum NetworkShowMode: String, CaseIterable {
|
||||||
|
case resource = "访问资源"
|
||||||
|
case device = "成员设备"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - 主网络视图
|
// MARK: - 主网络视图
|
||||||
@ -15,21 +23,67 @@ struct NetworkView: View {
|
|||||||
@Environment(\.openWindow) private var openWindow
|
@Environment(\.openWindow) private var openWindow
|
||||||
|
|
||||||
@State private var networkModel = NetworkModel()
|
@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 connectState: ConnectState = .disconnected
|
||||||
@State private var isConnecting: Bool = false
|
@State private var isConnecting: Bool = false
|
||||||
|
|
||||||
private var vpnManager = VPNManager.shared
|
private var vpnManager = VPNManager.shared
|
||||||
|
|
||||||
enum ShowMode: String, CaseIterable {
|
|
||||||
case resource = "访问资源"
|
|
||||||
case device = "成员设备"
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
// 1. 头部区域 (Header)
|
// 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()
|
Divider()
|
||||||
|
|
||||||
@ -39,9 +93,52 @@ struct NetworkView: View {
|
|||||||
case .waitAuth:
|
case .waitAuth:
|
||||||
NetworkWaitAuthView(networkModel: networkModel)
|
NetworkWaitAuthView(networkModel: networkModel)
|
||||||
case .connected:
|
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:
|
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)
|
.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) {
|
private func syncState(_ status: VPNManager.VPNStatus) {
|
||||||
switch status {
|
switch status {
|
||||||
|
|||||||
@ -64,7 +64,7 @@ struct punchnetApp: App {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
.windowResizability(.contentSize)
|
//.windowResizability(.contentSize)
|
||||||
.windowToolbarStyle(.unified)
|
.windowToolbarStyle(.unified)
|
||||||
.defaultPosition(.center)
|
.defaultPosition(.center)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user