feat: 上传素材

This commit is contained in:
jinyaqiu 2025-08-20 13:15:55 +08:00
parent 6a05bd0dc2
commit 4b11885d21
4 changed files with 98 additions and 59 deletions

View File

@ -507,16 +507,49 @@ struct PhotoPicker: UIViewControllerRepresentable {
case .success(let results): case .success(let results):
uploadResults[index] = results uploadResults[index] = results
// Upload file info to backend // 使 NetworkService
MaterialService.shared.uploadMaterialInfo( let parameters: [String: Any] = [
fileId: results.original.fileId, "file_id": results.original.fileId,
previewFileId: results.compressed.fileId "preview_file_id": results.compressed.fileId
) { success, errorMessage in ]
if success {
print("✅ 文件信息上传成功") //
} else if let errorMessage = errorMessage { let requestBody: [[String: Any]] = [parameters]
print("❌ 文件信息上传失败: \(errorMessage)")
//
struct MaterialResponse: Decodable {
let success: Bool
let message: String?
}
// 使 URLSession
do {
let jsonData = try JSONSerialization.data(withJSONObject: requestBody)
var request = URLRequest(url: URL(string: "\(APIConfig.baseURL)/material")!)
request.httpMethod = "POST"
request.allHTTPHeaderFields = APIConfig.authHeaders
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("❌ 文件信息上传失败: \(error.localizedDescription)")
return
}
if let httpResponse = response as? HTTPURLResponse {
if (200...299).contains(httpResponse.statusCode) {
print("✅ 文件信息上传成功")
} else {
let statusCode = httpResponse.statusCode
let errorMessage = String(data: data ?? Data(), encoding: .utf8) ?? ""
print("❌ 服务器返回错误状态码: \(statusCode), 响应: \(errorMessage)")
}
}
} }
task.resume()
} catch {
print("❌ 请求数据序列化失败: \(error.localizedDescription)")
} }
case .failure(let error): case .failure(let error):

View File

@ -17,10 +17,12 @@ class MaterialService {
previewFileId: String, previewFileId: String,
completion: @escaping (Bool, String?) -> Void) { completion: @escaping (Bool, String?) -> Void) {
let materialData: [[String: String]] = [[ let materialData: [String: Any] = [
"file_id": fileId, "material": [
"preview_file_id": previewFileId "file_id": fileId,
]] "preview_file_id": previewFileId
]
]
guard let url = URL(string: "\(APIConfig.baseURL)/material") else { guard let url = URL(string: "\(APIConfig.baseURL)/material") else {
completion(false, "无效的URL") completion(false, "无效的URL")
@ -46,7 +48,8 @@ class MaterialService {
completion(true, nil) completion(true, nil)
} else { } else {
let statusCode = httpResponse.statusCode let statusCode = httpResponse.statusCode
completion(false, "服务器返回错误状态码: \(statusCode)") let errorMessage = String(data: data ?? Data(), encoding: .utf8) ?? ""
completion(false, "服务器返回错误状态码: \(statusCode), 响应: \(errorMessage)")
} }
} else { } else {
completion(false, "无效的服务器响应") completion(false, "无效的服务器响应")

View File

@ -10,7 +10,6 @@ struct UserInfo: View {
@State private var darkModeEnabled = false @State private var darkModeEnabled = false
@State private var showLogoutAlert = false @State private var showLogoutAlert = false
@State private var avatarImage: UIImage? // Add this line @State private var avatarImage: UIImage? // Add this line
@State private var name: String = ""
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
@ -40,49 +39,53 @@ struct UserInfo: View {
.font(Typography.font(for: .title)) .font(Typography.font(for: .title))
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
// Avatar section // Avatar
VStack { ZStack {
Text("your name") // Show either the SVG or the uploaded image
.font(.headline) if let avatarImage = avatarImage {
.padding(.bottom, 10) Image(uiImage: avatarImage)
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.clipShape(Circle())
} else {
SVGImage(svgName: "Avatar")
.frame(width: 200, height: 200)
}
// Avatar image or placeholder // Make sure the AvatarUploader is on top and covers the entire area
Circle() AvatarUploader(selectedImage: $avatarImage, size: 200)
.fill(Color.gray.opacity(0.3)) .contentShape(Rectangle()) // This makes the entire area tappable
.frame(width: 100, height: 100)
.overlay(
Image(systemName: "person.fill")
.resizable()
.scaledToFit()
.foregroundColor(.white)
.padding(30)
)
} }
.padding(.top, 30) .frame(width: 200, height: 200)
.padding(.vertical, 20)
// Name input field // Buttons
TextField("Enter your name", text: $name)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.horizontal, 40)
.padding(.top, 20)
Spacer()
// Next/Open button
Button(action: { Button(action: {
// Action for open button // Action for first button
}) { }) {
Text("Open") Text("Upload from Gallery")
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.padding() .padding()
.foregroundColor(.black) .foregroundColor(.black)
.background( .background(
RoundedRectangle(cornerRadius: 25) RoundedRectangle(cornerRadius: 25)
.fill(Color(red: 1.0, green: 0.714, blue: 0.271)) .fill(Color(red: 1.0, green: 0.973, blue: 0.871))
)
}
Button(action: {
// Action for second button
}) {
Text("Take a Photo")
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 25)
.fill(Color(red: 1.0, green: 0.973, blue: 0.871))
) )
} }
.padding(.bottom, 30)
.padding(.horizontal, 20)
} }
.padding() .padding()
.background(Color(.white)) .background(Color(.white))

View File

@ -43,8 +43,8 @@ struct WakeApp: App {
} else { } else {
// //
if authState.isAuthenticated { if authState.isAuthenticated {
// // userInfo
ContentView() UserInfo()
.environmentObject(authState) .environmentObject(authState)
} else { } else {
// //
@ -54,12 +54,12 @@ struct WakeApp: App {
} }
} }
.onAppear { .onAppear {
// 3 //3
// DispatchQueue.main.asyncAfter(deadline: .now() + 3) { DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
// withAnimation { withAnimation {
// showSplash = false showSplash = false
// } }
// } }
} }
} }
.modelContainer(container) .modelContainer(container)
@ -91,10 +91,10 @@ struct WakeApp: App {
} }
// 3 // 3
// DispatchQueue.main.asyncAfter(deadline: .now() + 3) { DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
// withAnimation { withAnimation {
// showSplash = false showSplash = false
// } }
// } }
} }
} }