fix
This commit is contained in:
parent
7872604857
commit
2f2c5420e2
@ -19,11 +19,12 @@ class AppContext {
|
||||
var vpnOptions: [String: NSObject]? = nil
|
||||
|
||||
// 当前app所处的场景
|
||||
var loginScene: LoginScene = .login(username: nil)
|
||||
var appScene: AppScene = .login(username: nil)
|
||||
|
||||
// 当前的场景
|
||||
enum LoginScene: Equatable {
|
||||
enum AppScene: Equatable {
|
||||
case login(username: String?)
|
||||
case logined
|
||||
case register
|
||||
case resetPassword
|
||||
}
|
||||
|
||||
@ -19,63 +19,67 @@ struct LoginView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
// 顶部 Logo 区域
|
||||
VStack(spacing: 12) {
|
||||
ZStack {
|
||||
Color.clear
|
||||
|
||||
VStack(spacing: 0) {
|
||||
// 顶部 Logo 区域
|
||||
VStack(spacing: 12) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(Color.accentColor.opacity(0.1))
|
||||
.frame(width: 80, height: 80)
|
||||
|
||||
Image(systemName: "network") // 建议使用 SF Symbol 保持精致感
|
||||
.font(.system(size: 38, weight: .semibold))
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
|
||||
Text("PunchNet")
|
||||
.font(.system(size: 24, weight: .bold, design: .rounded))
|
||||
.tracking(1)
|
||||
}
|
||||
.padding(.top, 40)
|
||||
.padding(.bottom, 30)
|
||||
|
||||
// 原生分段切换器
|
||||
Picker("", selection: $authMethod) {
|
||||
ForEach(AuthMethod.allCases, id: \.self) { method in
|
||||
Text(method.rawValue).tag(method)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.frame(width: 220)
|
||||
.padding(.bottom, 30)
|
||||
|
||||
// 动态内容区
|
||||
ZStack {
|
||||
switch authMethod {
|
||||
case .account:
|
||||
LoginAccountView(username: self.username ?? "")
|
||||
.transition(.move(edge: .leading).combined(with: .opacity))
|
||||
case .token:
|
||||
LoginTokenView()
|
||||
.transition(.move(edge: .trailing).combined(with: .opacity))
|
||||
}
|
||||
}
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: authMethod)
|
||||
.frame(height: 180)
|
||||
|
||||
Spacer()
|
||||
|
||||
// 底部页脚
|
||||
HStack(spacing: 4) {
|
||||
Circle()
|
||||
.fill(Color.accentColor.opacity(0.1))
|
||||
.frame(width: 80, height: 80)
|
||||
.fill(Color.green)
|
||||
.frame(width: 8, height: 8)
|
||||
|
||||
Image(systemName: "network") // 建议使用 SF Symbol 保持精致感
|
||||
.font(.system(size: 38, weight: .semibold))
|
||||
.foregroundColor(.accentColor)
|
||||
Text("服务状态正常")
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Text("PunchNet")
|
||||
.font(.system(size: 24, weight: .bold, design: .rounded))
|
||||
.tracking(1)
|
||||
.padding(.bottom, 20)
|
||||
}
|
||||
.padding(.top, 40)
|
||||
.padding(.bottom, 30)
|
||||
|
||||
// 原生分段切换器
|
||||
Picker("", selection: $authMethod) {
|
||||
ForEach(AuthMethod.allCases, id: \.self) { method in
|
||||
Text(method.rawValue).tag(method)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.frame(width: 220)
|
||||
.padding(.bottom, 30)
|
||||
|
||||
// 动态内容区
|
||||
ZStack {
|
||||
switch authMethod {
|
||||
case .account:
|
||||
LoginAccountView(username: self.username ?? "")
|
||||
.transition(.move(edge: .leading).combined(with: .opacity))
|
||||
case .token:
|
||||
LoginTokenView()
|
||||
.transition(.move(edge: .trailing).combined(with: .opacity))
|
||||
}
|
||||
}
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: authMethod)
|
||||
.frame(height: 180)
|
||||
|
||||
Spacer()
|
||||
|
||||
// 底部页脚
|
||||
HStack(spacing: 4) {
|
||||
Circle()
|
||||
.fill(Color.green)
|
||||
.frame(width: 8, height: 8)
|
||||
|
||||
Text("服务状态正常")
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.padding(.bottom, 20)
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,10 +93,6 @@ struct LoginAccountView: View {
|
||||
@State private var password: String = ""
|
||||
@State private var isLoading = false
|
||||
|
||||
// 1. 引入环境操作
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Environment(\.openWindow) private var openWindow
|
||||
|
||||
// 错误提示
|
||||
@State private var showAlert: Bool = false
|
||||
@State private var errorMessage: String = ""
|
||||
@ -109,7 +109,7 @@ struct LoginAccountView: View {
|
||||
HStack {
|
||||
Button("注册") {
|
||||
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||
self.appContext.loginScene = .register
|
||||
self.appContext.appScene = .register
|
||||
}
|
||||
}
|
||||
.buttonStyle(.link)
|
||||
@ -118,7 +118,7 @@ struct LoginAccountView: View {
|
||||
|
||||
Button("忘记密码?") {
|
||||
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||
self.appContext.loginScene = .resetPassword
|
||||
self.appContext.appScene = .resetPassword
|
||||
}
|
||||
}
|
||||
.buttonStyle(.link)
|
||||
@ -173,10 +173,7 @@ struct LoginAccountView: View {
|
||||
do {
|
||||
_ = try await appContext.loginWith(username: username, password: password)
|
||||
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||
// 2. 先打开新窗口(顺序通常不影响,但先打开更稳妥)
|
||||
openWindow(id: "logined")
|
||||
// 3. 销毁当前窗口
|
||||
dismiss()
|
||||
self.appContext.appScene = .logined
|
||||
}
|
||||
|
||||
} catch let err as SDLAPIError {
|
||||
@ -193,10 +190,6 @@ struct LoginAccountView: View {
|
||||
struct LoginTokenView: View {
|
||||
@Environment(AppContext.self) var appContext: AppContext
|
||||
|
||||
// 1. 引入环境操作
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Environment(\.openWindow) private var openWindow
|
||||
|
||||
@State private var token = ""
|
||||
@State private var isLoading = false
|
||||
|
||||
@ -242,10 +235,7 @@ struct LoginTokenView: View {
|
||||
do {
|
||||
_ = try await appContext.loginWith(token: token)
|
||||
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||
// 2. 先打开新窗口(顺序通常不影响,但先打开更稳妥)
|
||||
openWindow(id: "logined")
|
||||
// 3. 销毁当前窗口
|
||||
dismiss()
|
||||
self.appContext.appScene = .logined
|
||||
}
|
||||
} catch let err as SDLAPIError {
|
||||
self.showAlert = true
|
||||
|
||||
@ -16,10 +16,13 @@ struct LoginRootView: View {
|
||||
// 1. 主要界面容器
|
||||
// 使用 ZStack 代替 Group,因为它在处理内容对齐和转场重叠时更稳定
|
||||
ZStack(alignment: .center) {
|
||||
switch appContext.loginScene {
|
||||
switch appContext.appScene {
|
||||
case .login(username: let username):
|
||||
LoginView(username: username)
|
||||
.id("scene_login") // 显式 ID 确保转场触发
|
||||
case .logined:
|
||||
NetworkView()
|
||||
.id("scene_logined")
|
||||
case .register:
|
||||
RegisterRootView()
|
||||
.id("scene_register")
|
||||
@ -28,10 +31,6 @@ struct LoginRootView: View {
|
||||
.id("scene_reset")
|
||||
}
|
||||
}
|
||||
// 2. 关键:设置容器尺寸行为
|
||||
.frame(width: 380, height: 500)
|
||||
// 3. 限制裁剪,防止位移动画超出 RootView 的边界
|
||||
.clipped()
|
||||
.transition(.asymmetric(
|
||||
insertion: .move(edge: .trailing).combined(with: .opacity),
|
||||
removal: .move(edge: .leading).combined(with: .opacity) // 修改为 leading 更有“流转”感
|
||||
@ -43,7 +42,7 @@ struct LoginRootView: View {
|
||||
}
|
||||
}
|
||||
// 4. 统一处理 Scene 切换的动画
|
||||
.animation(.spring(duration: 0.5), value: appContext.loginScene)
|
||||
.animation(.spring(duration: 0.5), value: appContext.appScene)
|
||||
.animation(.spring(duration: 0.4), value: updateManager.showUpdateOverlay)
|
||||
// 关键:macOS 标准毛玻璃背景
|
||||
.background(VisualEffectView(material: .hudWindow, blendingMode: .behindWindow))
|
||||
|
||||
@ -11,7 +11,6 @@ struct MainMenuBar: View {
|
||||
@State private var vpnManager = VPNManager.shared
|
||||
@Environment(AppContext.self) private var appContext: AppContext
|
||||
@Environment(\.openWindow) private var openWindow
|
||||
@Environment(\.dismissWindow) private var dismissWindow
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
@ -36,9 +35,9 @@ struct MainMenuBar: View {
|
||||
|
||||
Divider()
|
||||
|
||||
// Button("打开控制面板") {
|
||||
// openWindow(id: appContext.isLoggedIn ? "logined" : "login")
|
||||
// }
|
||||
Button("打开控制面板") {
|
||||
openWindow(id: "main")
|
||||
}
|
||||
|
||||
SettingsLink {
|
||||
Text("设置")
|
||||
@ -58,10 +57,6 @@ struct MainMenuBar: View {
|
||||
private func startVPN() async {
|
||||
if let options = appContext.vpnOptions {
|
||||
try? await vpnManager.enableVpn(options: options)
|
||||
dismissWindow(id: "login")
|
||||
openWindow(id: "logined")
|
||||
} else {
|
||||
openWindow(id: "login")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,6 @@ enum NetworkShowMode: String, CaseIterable {
|
||||
// MARK: - 主网络视图
|
||||
struct NetworkView: View {
|
||||
@Environment(AppContext.self) var appContext: AppContext
|
||||
@Environment(\.openWindow) private var openWindow
|
||||
|
||||
@State private var showMode: NetworkShowMode = .resource
|
||||
@State private var connectState: ConnectState = .disconnected
|
||||
|
||||
@ -44,7 +44,7 @@ struct RegisterRootView: View {
|
||||
Button(action: {
|
||||
// 执行返回逻辑,例如重置到登录
|
||||
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||
self.appContext.loginScene = .login(username: nil)
|
||||
self.appContext.appScene = .login(username: nil)
|
||||
}
|
||||
}) {
|
||||
HStack {
|
||||
@ -439,7 +439,7 @@ struct RegisterSuccessView: View {
|
||||
Button(action: {
|
||||
// 关闭当前注册窗口
|
||||
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||
self.appContext.loginScene = .login(username: registerModel.username)
|
||||
self.appContext.appScene = .login(username: registerModel.username)
|
||||
}
|
||||
}) {
|
||||
Text("立即开始使用")
|
||||
|
||||
@ -40,7 +40,7 @@ struct ResetPasswordRootView: View {
|
||||
Button(action: {
|
||||
// 执行返回逻辑,例如重置到登录
|
||||
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||
self.appContext.loginScene = .login(username: nil)
|
||||
self.appContext.appScene = .login(username: nil)
|
||||
}
|
||||
}) {
|
||||
HStack {
|
||||
@ -362,7 +362,7 @@ struct ResetPasswordSuccessView: View {
|
||||
|
||||
Button(action: {
|
||||
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||
self.appContext.loginScene = .login(username: self.resetPasswordModel.username)
|
||||
self.appContext.appScene = .login(username: self.resetPasswordModel.username)
|
||||
}
|
||||
}) {
|
||||
Text("返回登录")
|
||||
|
||||
@ -28,7 +28,7 @@ struct SettingsAccountView: View {
|
||||
} else {
|
||||
// 蓝色主按钮
|
||||
Button {
|
||||
self.openMainWindow(id: "login")
|
||||
self.openWindow(id: "main")
|
||||
} label: {
|
||||
Text("登录")
|
||||
.fontWeight(.medium)
|
||||
@ -44,23 +44,6 @@ struct SettingsAccountView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// 打开窗口
|
||||
private func openMainWindow(id: String) {
|
||||
let window = NSApp.windows.first { win in
|
||||
if let idStr = win.identifier?.rawValue {
|
||||
return idStr.starts(with: id)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if let window {
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
} else {
|
||||
openWindow(id: id)
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助头部组件
|
||||
private func sectionHeader(title: String, icon: String) -> some View {
|
||||
HStack {
|
||||
@ -117,7 +100,6 @@ extension SettingsAccountView {
|
||||
struct AccountCreditView: View {
|
||||
@Environment(AppContext.self) var appContext: AppContext
|
||||
@Environment(\.openWindow) var openWindow
|
||||
@Environment(\.dismissWindow) var dismissWindow
|
||||
|
||||
let username: String
|
||||
|
||||
@ -133,12 +115,7 @@ extension SettingsAccountView {
|
||||
Task { @MainActor in
|
||||
try await appContext.logout()
|
||||
}
|
||||
|
||||
self.dismissWindow(id: "logined")
|
||||
self.dismissWindow(id: "settings")
|
||||
|
||||
self.openWindow(id: "login")
|
||||
|
||||
self.openWindow(id: "main")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.foregroundColor(.red)
|
||||
@ -150,7 +127,6 @@ extension SettingsAccountView {
|
||||
struct TokenCreditView: View {
|
||||
@Environment(AppContext.self) var appContext: AppContext
|
||||
@Environment(\.openWindow) var openWindow
|
||||
@Environment(\.dismissWindow) var dismissWindow
|
||||
|
||||
let token: String
|
||||
|
||||
@ -160,11 +136,7 @@ extension SettingsAccountView {
|
||||
Task { @MainActor in
|
||||
try await appContext.logout()
|
||||
}
|
||||
|
||||
self.dismissWindow(id: "logined")
|
||||
self.dismissWindow(id: "settings")
|
||||
|
||||
self.openWindow(id: "login")
|
||||
self.openWindow(id: "main")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.foregroundColor(.red)
|
||||
|
||||
@ -27,7 +27,6 @@ struct punchnetApp: App {
|
||||
}()
|
||||
*/
|
||||
|
||||
@Environment(\.openWindow) private var openWindow
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
|
||||
private var noticeServer: UDPNoticeCenterServer
|
||||
@ -45,10 +44,16 @@ struct punchnetApp: App {
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
Window("登陆", id: "login") {
|
||||
Window("Punchnet", id: "main") {
|
||||
LoginRootView()
|
||||
.navigationTitle("")
|
||||
.environment(self.appContext)
|
||||
.frame(
|
||||
width: self.appContext.isLogined ? 900 : 380,
|
||||
height: self.appContext.isLogined ? 600 : 500
|
||||
)
|
||||
// 增加动画:当状态改变时,窗口会像微信那样丝滑地变大/变小
|
||||
.animation(.spring(response: 0.4, dampingFraction: 0.8), value: self.appContext.isLogined)
|
||||
.onAppear {
|
||||
self.showPrivacy = !hasAcceptedPrivacy
|
||||
}
|
||||
@ -60,14 +65,7 @@ struct punchnetApp: App {
|
||||
.windowToolbarStyle(.unified)
|
||||
.windowResizability(.contentSize)
|
||||
.defaultPosition(.center)
|
||||
|
||||
Window("网络", id: "logined") {
|
||||
NetworkView()
|
||||
.environment(self.appContext)
|
||||
.frame(width: 750, height: 500)
|
||||
}
|
||||
.windowResizability(.contentSize)
|
||||
.defaultPosition(.center)
|
||||
.windowStyle(.hiddenTitleBar)
|
||||
|
||||
Settings {
|
||||
SettingsView()
|
||||
@ -77,7 +75,7 @@ struct punchnetApp: App {
|
||||
.windowResizability(.contentSize)
|
||||
.defaultPosition(.center)
|
||||
|
||||
MenuBarExtra("punchnet", image: "logo_32") {
|
||||
MenuBarExtra("Punchnet", image: "logo_32") {
|
||||
MainMenuBar()
|
||||
.environment(appContext)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user