
- 在swift-assistant-style.mdc中添加项目背景、代码结构、命名规范、Swift最佳实践、UI开发、性能、安全性、测试与质量、核心功能、开发流程、App Store指南等详细规则。 - 在yanaApp.swift中将SplashView替换为Splash,简化应用结构。
201 lines
6.8 KiB
Swift
201 lines
6.8 KiB
Swift
import SwiftUI
|
|
|
|
// MARK: - 背景视图组件
|
|
struct LoginBackgroundView: View {
|
|
var body: some View {
|
|
Image("bg")
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fill)
|
|
.ignoresSafeArea(.all)
|
|
}
|
|
}
|
|
|
|
// MARK: - 顶部导航栏组件
|
|
struct LoginHeaderView: View {
|
|
let onBack: () -> Void
|
|
|
|
var body: some View {
|
|
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)
|
|
}
|
|
}
|
|
|
|
// MARK: - 通用输入框组件
|
|
enum InputFieldType {
|
|
case text
|
|
case number
|
|
case password
|
|
case verificationCode
|
|
}
|
|
|
|
struct CustomInputField: View {
|
|
let type: InputFieldType
|
|
let placeholder: String
|
|
let text: Binding<String>
|
|
let isPasswordVisible: Binding<Bool>?
|
|
let onGetCode: (() -> Void)?
|
|
let isCodeButtonEnabled: Bool
|
|
let isCodeLoading: Bool
|
|
let getCodeButtonText: String
|
|
|
|
init(
|
|
type: InputFieldType,
|
|
placeholder: String,
|
|
text: Binding<String>,
|
|
isPasswordVisible: Binding<Bool>? = nil,
|
|
onGetCode: (() -> Void)? = nil,
|
|
isCodeButtonEnabled: Bool = false,
|
|
isCodeLoading: Bool = false,
|
|
getCodeButtonText: String = ""
|
|
) {
|
|
self.type = type
|
|
self.placeholder = placeholder
|
|
self.text = text
|
|
self.isPasswordVisible = isPasswordVisible
|
|
self.onGetCode = onGetCode
|
|
self.isCodeButtonEnabled = isCodeButtonEnabled
|
|
self.isCodeLoading = isCodeLoading
|
|
self.getCodeButtonText = getCodeButtonText
|
|
}
|
|
|
|
var body: some View {
|
|
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 {
|
|
// 输入框
|
|
Group {
|
|
switch type {
|
|
case .text, .number:
|
|
TextField("", text: text)
|
|
.placeholder(when: text.wrappedValue.isEmpty) {
|
|
Text(placeholder)
|
|
.foregroundColor(.white.opacity(0.6))
|
|
}
|
|
.keyboardType(type == .number ? .numberPad : .default)
|
|
case .password:
|
|
if let isPasswordVisible = isPasswordVisible {
|
|
if isPasswordVisible.wrappedValue {
|
|
TextField("", text: text)
|
|
.placeholder(when: text.wrappedValue.isEmpty) {
|
|
Text(placeholder)
|
|
.foregroundColor(.white.opacity(0.6))
|
|
}
|
|
} else {
|
|
SecureField("", text: text)
|
|
.placeholder(when: text.wrappedValue.isEmpty) {
|
|
Text(placeholder)
|
|
.foregroundColor(.white.opacity(0.6))
|
|
}
|
|
}
|
|
}
|
|
case .verificationCode:
|
|
TextField("", text: text)
|
|
.placeholder(when: text.wrappedValue.isEmpty) {
|
|
Text(placeholder)
|
|
.foregroundColor(.white.opacity(0.6))
|
|
}
|
|
.keyboardType(.numberPad)
|
|
}
|
|
}
|
|
.foregroundColor(.white)
|
|
.font(.system(size: 16))
|
|
|
|
// 右侧按钮
|
|
if type == .password, let isPasswordVisible = isPasswordVisible {
|
|
Button(action: {
|
|
isPasswordVisible.wrappedValue.toggle()
|
|
}) {
|
|
Image(systemName: isPasswordVisible.wrappedValue ? "eye.slash" : "eye")
|
|
.foregroundColor(.white.opacity(0.7))
|
|
.font(.system(size: 18))
|
|
}
|
|
} else if type == .verificationCode, let onGetCode = onGetCode {
|
|
Button(action: onGetCode) {
|
|
ZStack {
|
|
if isCodeLoading {
|
|
ProgressView()
|
|
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
|
.scaleEffect(0.7)
|
|
} else {
|
|
Text(getCodeButtonText)
|
|
.font(.system(size: 14, weight: .medium))
|
|
.foregroundColor(.white)
|
|
}
|
|
}
|
|
.frame(width: 60, height: 36)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: 15)
|
|
.fill(Color.white.opacity(isCodeButtonEnabled ? 0.2 : 0.1))
|
|
)
|
|
}
|
|
.disabled(!isCodeButtonEnabled || isCodeLoading)
|
|
}
|
|
}
|
|
.padding(.horizontal, 24)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 登录按钮组件
|
|
struct LoginButtonView: View {
|
|
let isLoading: Bool
|
|
let isEnabled: Bool
|
|
let onTap: () -> Void
|
|
|
|
var body: some View {
|
|
Button(action: onTap) {
|
|
Group {
|
|
if isLoading {
|
|
ProgressView()
|
|
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
|
.scaleEffect(1.2)
|
|
} else {
|
|
Text("Login")
|
|
.font(.system(size: 16, weight: .medium))
|
|
.foregroundColor(.white)
|
|
}
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
.padding(.vertical, 16)
|
|
.background(isEnabled ? Color(red: 0.5, green: 0.3, blue: 0.8) : Color.gray)
|
|
.cornerRadius(8)
|
|
.disabled(!isEnabled)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
VStack(spacing: 20) {
|
|
LoginBackgroundView()
|
|
|
|
LoginHeaderView(onBack: {})
|
|
|
|
CustomInputField(
|
|
type: .text,
|
|
placeholder: "Test Input",
|
|
text: .constant("")
|
|
)
|
|
|
|
LoginButtonView(
|
|
isLoading: false,
|
|
isEnabled: true,
|
|
onTap: {}
|
|
)
|
|
}
|
|
} |