Compare commits

...

2 Commits

Author SHA1 Message Date
1026bbb987 refactor: 重构盲盒页面 2025-09-05 19:12:30 +08:00
ea4a5617ec feat: api地址变更 2025-09-05 18:28:52 +08:00
6 changed files with 139 additions and 3 deletions

View File

@ -3,7 +3,7 @@ import Foundation
/// API /// API
public enum APIConfig { public enum APIConfig {
/// API URL /// API URL
public static let baseURL = "https://api.memorywake.com:31274/api/v1" public static let baseURL = "https://api.memorywake.com/api/v1"
/// token - Keychain /// token - Keychain
public static var authToken: String { public static var authToken: String {

View File

@ -11,7 +11,7 @@ import Foundation
//enum AnyCodable: Codable {} //enum AnyCodable: Codable {}
func passwordLogin(username: String, password: String) { func passwordLogin(username: String, password: String) {
guard let url = URL(string: "https://api.memorywake.com:31274/api/v1/iam/login/password-login") else { guard let url = URL(string: "https://api.memorywake.com/api/v1/iam/login/password-login") else {
print("❌ 无效的URL") print("❌ 无效的URL")
return return
} }

View File

@ -48,6 +48,19 @@ extension NetworkService: NetworkServiceProtocol {
post(path: path, parameters: parameters, headers: headers, completion: completion) post(path: path, parameters: parameters, headers: headers, completion: completion)
} }
public func getWithToken<T: Decodable>(
path: String,
parameters: [String: Any]? = nil,
completion: @escaping (Result<T, NetworkError>) -> Void
) {
var headers = [String: String]()
if let token = KeychainHelper.getAccessToken() {
headers["Authorization"] = "Bearer \(token)"
}
get(path: path, parameters: parameters, headers: headers, completion: completion)
}
@discardableResult @discardableResult
public func upload( public func upload(
request: URLRequest, request: URLRequest,

View File

@ -7,7 +7,7 @@ struct LoginResponse: Codable {
func callLoginAPI() { func callLoginAPI() {
// 1. URL // 1. URL
let urlString = "https://api.memorywake.com:31274/api/v1/iam/login/password-login" let urlString = "https://api.memorywake.com/api/v1/iam/login/password-login"
guard let url = URL(string: urlString) else { guard let url = URL(string: urlString) else {
print("Invalid URL") print("Invalid URL")
return return

View File

@ -0,0 +1,47 @@
// MARK: - Blind Box Controls
import SwiftUI
struct BlindBoxControls: View {
let mediaType: BlindBoxMediaType
let animationPhase: BlindBoxAnimationPhase
let countdown: (minutes: Int, seconds: Int, milliseconds: Int)
let onButtonTap: () -> Void
let onCountdownStart: () -> Void
var body: some View {
if mediaType == .all {
Button(action: onButtonTap) {
if animationPhase == .loading {
Text("Next: \(countdown.minutes):\(String(format: "%02d", countdown.seconds)).\(String(format: "%02d", countdown.milliseconds))")
.font(Typography.font(for: .body))
.fontWeight(.bold)
.frame(maxWidth: .infinity)
.padding()
.background(Color.white)
.foregroundColor(.black)
.cornerRadius(32)
.onAppear(perform: onCountdownStart)
} else if animationPhase == .ready {
Text("Ready")
.font(Typography.font(for: .body))
.fontWeight(.bold)
.frame(maxWidth: .infinity)
.padding()
.background(Color.themePrimary)
.foregroundColor(Color.themeTextMessageMain)
.cornerRadius(32)
} else {
Text("Go to Buy")
.font(Typography.font(for: .body))
.fontWeight(.bold)
.frame(maxWidth: .infinity)
.padding()
.background(Color.themePrimary)
.foregroundColor(Color.themeTextMessageMain)
.cornerRadius(32)
}
}
.padding(.horizontal)
}
}
}

View File

@ -0,0 +1,76 @@
// MARK: - Media Overlay View
import SwiftUI
import AVKit
struct MediaOverlayView: View {
@Binding var videoPlayer: AVPlayer?
@Binding var showControls: Bool
let mediaType: BlindBoxMediaType
let displayImage: UIImage?
let scaledWidth: CGFloat
let scaledHeight: CGFloat
let scale: CGFloat
let videoURL: String
let imageURL: String
let blindGenerate: BlindBoxData?
let onBackTap: () -> Void
var body: some View {
ZStack {
VisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialLight))
.opacity(0.3)
.edgesIgnoringSafeArea(.all)
Group {
if mediaType == .video, let player = videoPlayer {
AVPlayerController(player: $videoPlayer)
.frame(width: scaledWidth, height: scaledHeight)
.opacity(scale == 1 ? 1 : 0.7)
.onAppear { player.play() }
} else if mediaType == .image, let image = displayImage {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: scaledWidth, height: scaledHeight)
.opacity(scale == 1 ? 1 : 0.7)
}
}
.onTapGesture {
withAnimation(.easeInOut(duration: 0.1)) {
showControls.toggle()
}
}
//
if showControls {
VStack {
HStack {
Button(action: onBackTap) {
Image(systemName: "chevron.left")
.font(.system(size: 24))
.foregroundColor(.black)
}
Spacer()
}
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding(.top, 50)
.padding(.leading, 20)
.zIndex(1000)
.transition(.opacity)
.onAppear {
// 2
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation(.easeInOut(duration: 0.3)) {
// showControls = true
}
}
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.animation(.easeInOut(duration: 1.0), value: scale)
.ignoresSafeArea()
}
}