import SwiftUI import ComposableArchitecture struct IDLoginView: View { let store: StoreOf let onBack: () -> Void // 使用本地@State管理UI状态 @State private var userID: String = "" @State private var password: String = "" @State private var isPasswordVisible: Bool = false @State private var showRecoverPassword: Bool = false // 计算登录按钮是否可用 private var isLoginButtonEnabled: Bool { return !store.isLoading && !userID.isEmpty && !password.isEmpty } var body: some View { GeometryReader { geometry in ZStack { // 背景图片 - 使用与登录页面相同的"bg" Image("bg") .resizable() .aspectRatio(contentMode: .fill) .ignoresSafeArea(.all) VStack(spacing: 0) { // 顶部导航栏 HStack { Button(action: { onBack() }) { Image(systemName: "chevron.left") .font(.system(size: 24, weight: .medium)) .foregroundColor(.white) .frame(width: 44, height: 44) } Spacer() } .padding(.horizontal, 16) .padding(.top, 8) Spacer() .frame(height: 60) // 标题 Text("id_login.title".localized) .font(.system(size: 28, weight: .medium)) .foregroundColor(.white) .padding(.bottom, 80) // 输入框区域 VStack(spacing: 24) { // ID 输入框 ZStack { RoundedRectangle(cornerRadius: 25) .fill(Color.white.opacity(0.1)) .overlay( RoundedRectangle(cornerRadius: 25) .stroke(Color.white.opacity(0.3), lineWidth: 1) ) .frame(height: 56) TextField("", text: $userID) // 使用SwiftUI的绑定 .placeholder(when: userID.isEmpty) { Text("placeholder.enter_id".localized) .foregroundColor(.white.opacity(0.6)) } .foregroundColor(.white) .font(.system(size: 16)) .padding(.horizontal, 24) .keyboardType(.numberPad) } // 密码输入框 ZStack { RoundedRectangle(cornerRadius: 25) .fill(Color.white.opacity(0.1)) .overlay( RoundedRectangle(cornerRadius: 25) .stroke(Color.white.opacity(0.3), lineWidth: 1) ) .frame(height: 56) HStack { if isPasswordVisible { TextField("", text: $password) // 使用SwiftUI的绑定 .placeholder(when: password.isEmpty) { Text("placeholder.enter_password".localized) .foregroundColor(.white.opacity(0.6)) } .foregroundColor(.white) .font(.system(size: 16)) } else { SecureField("", text: $password) // 使用SwiftUI的绑定 .placeholder(when: password.isEmpty) { Text("placeholder.enter_password".localized) .foregroundColor(.white.opacity(0.6)) } .foregroundColor(.white) .font(.system(size: 16)) } Button(action: { isPasswordVisible.toggle() }) { Image(systemName: isPasswordVisible ? "eye.slash" : "eye") .foregroundColor(.white.opacity(0.7)) .font(.system(size: 18)) } } .padding(.horizontal, 24) } } .padding(.horizontal, 32) // Forgot Password 链接 HStack { Spacer() Button(action: { showRecoverPassword = true }) { Text("id_login.forgot_password".localized) .font(.system(size: 14)) .foregroundColor(.white.opacity(0.8)) } } .padding(.horizontal, 32) .padding(.top, 16) Spacer() .frame(height: 60) // 登录按钮 Button(action: { // 发送登录action时传递本地状态 store.send(.loginButtonTapped(userID: userID, password: password)) }) { ZStack { // 渐变背景 LinearGradient( colors: [ Color(red: 0.85, green: 0.37, blue: 1.0), // #D85EFF Color(red: 0.54, green: 0.31, blue: 1.0) // #8A4FFF ], startPoint: .leading, endPoint: .trailing ) .clipShape(RoundedRectangle(cornerRadius: 28)) HStack { if store.isLoading { ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white)) .scaleEffect(0.8) } Text(store.isLoading ? "id_login.logging_in".localized : "id_login.login_button".localized) .font(.system(size: 18, weight: .semibold)) .foregroundColor(.white) } } .frame(height: 56) } .disabled(store.isLoading || userID.isEmpty || password.isEmpty) .opacity(isLoginButtonEnabled ? 1.0 : 0.5) // 透明度50%当条件不满足时 .padding(.horizontal, 32) // 错误信息 if let errorMessage = store.errorMessage { Text(errorMessage) .font(.system(size: 14)) .foregroundColor(.red) .padding(.top, 16) .padding(.horizontal, 32) } Spacer() } // 隐藏的NavigationLink - 导航到密码恢复页面 NavigationLink( destination: RecoverPasswordView( store: Store( initialState: RecoverPasswordFeature.State() ) { RecoverPasswordFeature() }, onBack: { showRecoverPassword = false } ) .navigationBarHidden(true), isActive: $showRecoverPassword ) { EmptyView() } .hidden() } } .onAppear { // 初始化时同步TCA状态到本地状态 userID = store.userID password = store.password isPasswordVisible = store.isPasswordVisible #if DEBUG // 移除测试用的硬编码凭据 debugInfo("🐛 Debug模式: 已移除硬编码测试凭据") #endif } } } #Preview { IDLoginView( store: Store( initialState: IDLoginFeature.State() ) { IDLoginFeature() }, onBack: {} ) }