
- 将iOS平台版本更新至17,确保与最新的开发环境兼容。 - 更新Podfile中的iOS部署目标至17.0,确保依赖项与新版本兼容。 - 修改Podfile.lock以反映新的依赖项版本,确保项目一致性。 - 在多个视图中重构代码,优化状态管理和视图逻辑,提升用户体验。
227 lines
10 KiB
Swift
227 lines
10 KiB
Swift
import SwiftUI
|
||
import ComposableArchitecture
|
||
import Perception
|
||
|
||
struct IDLoginView: View {
|
||
let store: StoreOf<IDLoginFeature>
|
||
let onBack: () -> Void
|
||
@Binding var showIDLogin: Bool // 新增:绑定父视图的显示状态
|
||
|
||
// 使用本地@State管理UI状态
|
||
@State private var userID: String = ""
|
||
@State private var password: String = ""
|
||
@State private var isPasswordVisible: Bool = false
|
||
|
||
// 导航状态管理 - 与 LoginView 保持一致
|
||
@State private var showRecoverPassword: Bool = false
|
||
|
||
// 计算登录按钮是否可用
|
||
private var isLoginButtonEnabled: Bool {
|
||
return !store.isLoading && !userID.isEmpty && !password.isEmpty
|
||
}
|
||
|
||
var body: some View {
|
||
WithPerceptionTracking {
|
||
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(LocalizedString("id_login.title", comment: ""))
|
||
.font(.system(size: 28, weight: .medium))
|
||
.foregroundColor(.white)
|
||
.padding(.bottom, 80)
|
||
|
||
// 输入框区域
|
||
VStack(spacing: 20) {
|
||
// 用户ID输入框
|
||
VStack(alignment: .leading, spacing: 8) {
|
||
HStack {
|
||
Image("id icon")
|
||
.resizable()
|
||
.aspectRatio(contentMode: .fit)
|
||
.frame(width: 20, height: 20)
|
||
Text(LocalizedString("id_login.user_id", comment: ""))
|
||
.font(.system(size: 16))
|
||
.foregroundColor(.white)
|
||
}
|
||
|
||
TextField("", text: $userID)
|
||
.textFieldStyle(PlainTextFieldStyle())
|
||
.font(.system(size: 16))
|
||
.foregroundColor(.white)
|
||
.padding(.horizontal, 16)
|
||
.padding(.vertical, 12)
|
||
.background(Color.white.opacity(0.1))
|
||
.cornerRadius(8)
|
||
.onChange(of: userID) { newValue in
|
||
store.send(.userIDChanged(newValue))
|
||
}
|
||
}
|
||
|
||
// 密码输入框
|
||
VStack(alignment: .leading, spacing: 8) {
|
||
HStack {
|
||
Image("email icon")
|
||
.resizable()
|
||
.aspectRatio(contentMode: .fit)
|
||
.frame(width: 20, height: 20)
|
||
Text(LocalizedString("id_login.password", comment: ""))
|
||
.font(.system(size: 16))
|
||
.foregroundColor(.white)
|
||
}
|
||
|
||
HStack {
|
||
if isPasswordVisible {
|
||
TextField("", text: $password)
|
||
.textFieldStyle(PlainTextFieldStyle())
|
||
} else {
|
||
SecureField("", text: $password)
|
||
.textFieldStyle(PlainTextFieldStyle())
|
||
}
|
||
|
||
Button(action: {
|
||
isPasswordVisible.toggle()
|
||
}) {
|
||
Image(systemName: isPasswordVisible ? "eye.slash" : "eye")
|
||
.foregroundColor(.white.opacity(0.7))
|
||
}
|
||
}
|
||
.font(.system(size: 16))
|
||
.foregroundColor(.white)
|
||
.padding(.horizontal, 16)
|
||
.padding(.vertical, 12)
|
||
.background(Color.white.opacity(0.1))
|
||
.cornerRadius(8)
|
||
.onChange(of: password) { newValue in
|
||
store.send(.passwordChanged(newValue))
|
||
}
|
||
}
|
||
|
||
// 忘记密码按钮
|
||
HStack {
|
||
Spacer()
|
||
Button(action: {
|
||
showRecoverPassword = true
|
||
}) {
|
||
Text(LocalizedString("id_login.forgot_password", comment: ""))
|
||
.font(.system(size: 14))
|
||
.foregroundColor(.white.opacity(0.8))
|
||
}
|
||
}
|
||
|
||
// 登录按钮
|
||
Button(action: {
|
||
store.send(.loginButtonTapped)
|
||
}) {
|
||
if store.isLoading {
|
||
ProgressView()
|
||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||
.scaleEffect(1.2)
|
||
} else {
|
||
Text(LocalizedString("id_login.login", comment: ""))
|
||
.font(.system(size: 16, weight: .medium))
|
||
.foregroundColor(.white)
|
||
}
|
||
}
|
||
.frame(maxWidth: .infinity)
|
||
.padding(.vertical, 16)
|
||
.background(isLoginButtonEnabled ? Color.blue : Color.gray)
|
||
.cornerRadius(8)
|
||
.disabled(!isLoginButtonEnabled)
|
||
.padding(.top, 20)
|
||
|
||
// 错误信息显示
|
||
if let errorMessage = store.errorMessage {
|
||
Text(errorMessage)
|
||
.font(.system(size: 14))
|
||
.foregroundColor(.red)
|
||
.padding(.top, 16)
|
||
.padding(.horizontal, 32)
|
||
}
|
||
|
||
Spacer()
|
||
}
|
||
|
||
// 添加API Loading和错误处理视图
|
||
APILoadingEffectView()
|
||
}
|
||
}
|
||
}
|
||
.navigationBarHidden(true)
|
||
// 使用与 LoginView 一致的 navigationDestination 方式
|
||
.navigationDestination(isPresented: $showRecoverPassword) {
|
||
WithPerceptionTracking {
|
||
RecoverPasswordView(
|
||
store: Store(
|
||
initialState: RecoverPasswordFeature.State()
|
||
) {
|
||
RecoverPasswordFeature()
|
||
},
|
||
onBack: {
|
||
showRecoverPassword = false
|
||
}
|
||
)
|
||
.navigationBarHidden(true)
|
||
}
|
||
}
|
||
.onAppear {
|
||
let _ = WithPerceptionTracking {
|
||
// 初始化时同步TCA状态到本地状态
|
||
userID = store.userID
|
||
password = store.password
|
||
isPasswordVisible = store.isPasswordVisible
|
||
|
||
#if DEBUG
|
||
// 移除测试用的硬编码凭据
|
||
debugInfoSync("🐛 Debug模式: 已移除硬编码测试凭据")
|
||
#endif
|
||
}
|
||
}
|
||
.onChange(of: store.loginStep) { newStep in
|
||
debugInfoSync("🔄 IDLoginView: loginStep 变化为 \(newStep)")
|
||
if newStep == .completed {
|
||
debugInfoSync("✅ IDLoginView: 登录成功,准备关闭自身")
|
||
showIDLogin = false
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//#Preview {
|
||
// IDLoginView(
|
||
// store: Store(
|
||
// initialState: IDLoginFeature.State()
|
||
// ) {
|
||
// IDLoginFeature()
|
||
// },
|
||
// onBack: {},
|
||
// showIDLogin: .constant(true)
|
||
// )
|
||
//}
|