import SwiftUI import CoreData import CoreImage // MARK: - 二维码创建界面 struct CreateQRCodeView: View { @Environment(\.dismiss) private var dismiss @StateObject private var coreDataManager = CoreDataManager.shared // 从类型选择界面传入的参数 let selectedQRCodeType: QRCodeType // 通用内容输入 @State private var content = "" @FocusState private var isContentFieldFocused: Bool // Email相关字段 @State private var emailAddress = "" @State private var emailSubject = "" @State private var emailBody = "" @State private var emailCc = "" @State private var emailBcc = "" @FocusState private var focusedEmailField: EmailInputView.EmailField? // WiFi相关字段 @State private var wifiSSID = "" @State private var wifiPassword = "" @State private var wifiEncryptionType: WiFiInputView.WiFiEncryptionType = .wpa2 @FocusState private var focusedWiFiField: WiFiInputView.WiFiField? // 联系人相关字段 @State private var contactFirstName = "" @State private var contactLastName = "" @State private var contactPhone = "" @State private var contactEmail = "" @State private var contactCompany = "" @State private var contactTitle = "" @State private var contactAddress = "" @State private var contactWebsite = "" @FocusState private var focusedContactField: ContactInputView.ContactField? // 位置相关字段 @State private var locationLatitude = "" @State private var locationLongitude = "" @State private var locationName = "" @FocusState private var focusedLocationField: LocationInputView.LocationField? // 日历相关字段 @State private var eventTitle = "" @State private var eventDescription = "" @State private var eventLocation = "" @State private var startDate = Date() @State private var endDate = Date().addingTimeInterval(3600) @FocusState private var focusedCalendarField: CalendarInputView.CalendarField? // 社交平台相关字段 @State private var socialUsername = "" @State private var socialMessage = "" @FocusState private var focusedSocialField: SocialInputView.SocialField? // 电话相关字段 @State private var phoneNumber = "" @State private var phoneMessage = "" @FocusState private var focusedPhoneField: PhoneInputView.PhoneField? // URL相关字段 @State private var urlString = "" @FocusState private var isURLFieldFocused: Bool // 通用状态 @State private var showingAlert = false @State private var alertMessage = "" var body: some View { VStack(spacing: 0) { inputAndPreviewSection } .navigationTitle(selectedQRCodeType.displayName) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("创建") { createQRCode() } .disabled(!canCreateQRCode()) .font(.system(size: 16, weight: .semibold)) } } .alert("提示", isPresented: $showingAlert) { Button("确定") { } } message: { Text(alertMessage) } .onAppear { setupInitialFocus() } .onTapGesture { hideKeyboard() } } // MARK: - UI Components private var inputAndPreviewSection: some View { ScrollView { VStack(spacing: 24) { // 输入提示 InputHintView.info( hint: getContentHint() ) .padding(.horizontal, 20) // 内容输入区域 VStack(spacing: 16) { InputTitleView.required( selectedQRCodeType == .mail ? "邮件信息" : "输入内容", icon: getInputIcon() ) .padding(.horizontal, 20) // 使用InputComponentFactory动态选择输入组件 InputComponentFactory.createInputComponent( for: selectedQRCodeType, content: $content, emailAddress: $emailAddress, emailSubject: $emailSubject, emailBody: $emailBody, emailCc: $emailCc, emailBcc: $emailBcc, focusedEmailField: _focusedEmailField, isContentFieldFocused: _isContentFieldFocused, ssid: $wifiSSID, password: $wifiPassword, encryptionType: $wifiEncryptionType, focusedWiFiField: _focusedWiFiField, firstName: $contactFirstName, lastName: $contactLastName, phone: $contactPhone, email: $contactEmail, company: $contactCompany, title: $contactTitle, address: $contactAddress, website: $contactWebsite, focusedContactField: _focusedContactField, latitude: $locationLatitude, longitude: $locationLongitude, locationName: $locationName, focusedLocationField: _focusedLocationField, eventTitle: $eventTitle, eventDescription: $eventDescription, startDate: $startDate, endDate: $endDate, location: $eventLocation, focusedCalendarField: _focusedCalendarField, username: $socialUsername, message: $socialMessage, focusedSocialField: _focusedSocialField, phoneNumber: $phoneNumber, phoneMessage: $phoneMessage, focusedPhoneField: _focusedPhoneField, url: $urlString, isUrlFieldFocused: _isURLFieldFocused ) .padding(.horizontal, 20) } // 预览区域 if canCreateQRCode() { VStack(spacing: 16) { InputTitleView.required("预览", icon: "eye") .padding(.horizontal, 20) // 使用QRCodePreviewView组件 QRCodePreviewView( qrCodeImage: generateQRCodeImage(), formattedContent: formatContentForQRCodeType(), qrCodeType: selectedQRCodeType ) .padding(.horizontal, 20) } } Spacer(minLength: 100) } .padding(.top, 20) } .background(Color(.systemGroupedBackground)) } // MARK: - Helper Methods private func setupInitialFocus() { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { switch selectedQRCodeType { case .mail: focusedEmailField = .address case .wifi: focusedWiFiField = .ssid case .vcard, .mecard: focusedContactField = .firstName case .location: focusedLocationField = .latitude case .calendar: focusedCalendarField = .title case .instagram, .facebook, .spotify, .twitter, .snapchat, .tiktok: focusedSocialField = .username case .phone, .sms: focusedPhoneField = .phoneNumber case .url: isURLFieldFocused = true default: isContentFieldFocused = true } } } private func hideKeyboard() { switch selectedQRCodeType { case .mail: focusedEmailField = nil case .wifi: focusedWiFiField = nil case .vcard, .mecard: focusedContactField = nil case .location: focusedLocationField = nil case .calendar: focusedCalendarField = nil case .instagram, .facebook, .spotify, .twitter, .snapchat, .tiktok: focusedSocialField = nil case .phone, .sms: focusedPhoneField = nil case .url: isURLFieldFocused = false default: isContentFieldFocused = false } } private func getInputIcon() -> String { switch selectedQRCodeType { case .mail: return "envelope" case .wifi: return "wifi" case .vcard, .mecard: return "person" case .location: return "location" case .calendar: return "calendar" case .instagram, .facebook, .spotify, .twitter, .snapchat, .tiktok: return "globe" case .phone, .sms: return "phone" case .url: return "link" default: return "textformat" } } private func canCreateQRCode() -> Bool { switch selectedQRCodeType { case .mail: return !emailAddress.isEmpty && !emailSubject.isEmpty && !emailBody.isEmpty case .wifi: return !wifiSSID.isEmpty case .vcard, .mecard: return !contactFirstName.isEmpty || !contactLastName.isEmpty case .location: return !locationLatitude.isEmpty && !locationLongitude.isEmpty case .calendar: return !eventTitle.isEmpty case .instagram, .facebook, .spotify, .twitter, .snapchat, .tiktok: return !socialUsername.isEmpty case .phone, .sms: return !phoneNumber.isEmpty case .url: return !urlString.isEmpty default: return !content.isEmpty } } private func getContentHint() -> String { InputComponentFactory.getPlaceholderText(for: selectedQRCodeType) } private func generateQRCodeImage() -> UIImage? { guard canCreateQRCode() else { return nil } let formattedContent = formatContentForQRCodeType() let data = formattedContent.data(using: .utf8) let qrFilter = CIFilter.qrCodeGenerator() qrFilter.setValue(data, forKey: "inputMessage") qrFilter.setValue("H", forKey: "inputCorrectionLevel") guard let outputImage = qrFilter.outputImage else { return nil } let context = CIContext() guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return nil } return UIImage(cgImage: cgImage) } private func formatContentForQRCodeType() -> String { switch selectedQRCodeType { case .text: return content case .url: return urlString.hasPrefix("http") ? urlString : "https://\(urlString)" case .mail: var mailtoURL = "mailto:\(emailAddress)" var queryParams: [String] = [] if !emailSubject.isEmpty { queryParams.append("subject=\(emailSubject.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? emailSubject)") } if !emailBody.isEmpty { queryParams.append("body=\(emailBody.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? emailBody)") } if !emailCc.isEmpty { queryParams.append("cc=\(emailCc)") } if !emailBcc.isEmpty { queryParams.append("bcc=\(emailBcc)") } if !queryParams.isEmpty { mailtoURL += "?" + queryParams.joined(separator: "&") } return mailtoURL case .phone: return "tel:\(phoneNumber)" case .sms: let smsContent = phoneMessage.isEmpty ? "Hello" : phoneMessage return "sms:\(phoneNumber):\(smsContent)" case .wifi: return "WIFI:T:\(wifiEncryptionType.rawValue);S:\(wifiSSID);P:\(wifiPassword);;" case .vcard: var vcard = "BEGIN:VCARD\nVERSION:3.0\n" if !contactFirstName.isEmpty || !contactLastName.isEmpty { vcard += "FN:\(contactFirstName) \(contactLastName)\n" } if !contactPhone.isEmpty { vcard += "TEL:\(contactPhone)\n" } if !contactEmail.isEmpty { vcard += "EMAIL:\(contactEmail)\n" } if !contactCompany.isEmpty { vcard += "ORG:\(contactCompany)\n" } if !contactTitle.isEmpty { vcard += "TITLE:\(contactTitle)\n" } if !contactAddress.isEmpty { vcard += "ADR:\(contactAddress)\n" } if !contactWebsite.isEmpty { vcard += "URL:\(contactWebsite)\n" } vcard += "END:VCARD" return vcard case .mecard: var mecard = "MECARD:" if !contactFirstName.isEmpty || !contactLastName.isEmpty { mecard += "N:\(contactLastName),\(contactFirstName);" } if !contactPhone.isEmpty { mecard += "TEL:\(contactPhone);" } if !contactEmail.isEmpty { mecard += "EMAIL:\(contactEmail);" } if !contactCompany.isEmpty { mecard += "ORG:\(contactCompany);" } if !contactAddress.isEmpty { mecard += "ADR:\(contactAddress);" } if !contactWebsite.isEmpty { mecard += "URL:\(contactWebsite);" } mecard += ";" return mecard case .location: let coords = "\(locationLatitude),\(locationLongitude)" return locationName.isEmpty ? "geo:\(coords)" : "geo:\(coords)?q=\(locationName)" case .calendar: let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'" dateFormatter.timeZone = TimeZone(abbreviation: "UTC") var ical = "BEGIN:VEVENT\n" ical += "SUMMARY:\(eventTitle)\n" if !eventDescription.isEmpty { ical += "DESCRIPTION:\(eventDescription)\n" } if !eventLocation.isEmpty { ical += "LOCATION:\(eventLocation)\n" } ical += "DTSTART:\(dateFormatter.string(from: startDate))\n" ical += "DTEND:\(dateFormatter.string(from: endDate))\n" ical += "END:VEVENT" return ical case .instagram: return "https://instagram.com/\(socialUsername)" case .facebook: return "https://facebook.com/\(socialUsername)" case .spotify: return socialUsername.hasPrefix("http") ? socialUsername : "https://open.spotify.com/track/\(socialUsername)" case .twitter: return "https://twitter.com/\(socialUsername)" case .whatsapp: let message = socialMessage.isEmpty ? "Hello" : socialMessage return "https://wa.me/\(socialUsername)?text=\(message.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? message)" case .viber: let message = socialMessage.isEmpty ? "Hello" : socialMessage return "viber://chat?number=\(socialUsername)&text=\(message.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? message)" case .snapchat: return "https://snapchat.com/add/\(socialUsername)" case .tiktok: return "https://tiktok.com/@\(socialUsername)" } } private func createQRCode() { let context = coreDataManager.container.viewContext let historyItem = HistoryItem(context: context) historyItem.id = UUID() historyItem.dataType = DataType.qrcode.rawValue historyItem.dataSource = DataSource.created.rawValue historyItem.createdAt = Date() historyItem.isFavorite = false historyItem.qrCodeType = selectedQRCodeType.rawValue // 根据类型设置内容 switch selectedQRCodeType { case .mail: var mailContent = "邮箱: \(emailAddress)\n主题: \(emailSubject)\n正文: \(emailBody)" if !emailCc.isEmpty { mailContent += "\n抄送: \(emailCc)" } if !emailBcc.isEmpty { mailContent += "\n密送: \(emailBcc)" } historyItem.content = mailContent case .wifi: historyItem.content = "WiFi: \(wifiSSID) (\(wifiEncryptionType.displayName))" case .vcard, .mecard: historyItem.content = "联系人: \(contactFirstName) \(contactLastName)" case .location: historyItem.content = "位置: \(locationLatitude), \(locationLongitude)" case .calendar: historyItem.content = "事件: \(eventTitle)" case .instagram, .facebook, .spotify, .twitter, .snapchat, .tiktok: historyItem.content = "\(selectedQRCodeType.displayName): \(socialUsername)" case .phone, .sms: historyItem.content = "电话: \(phoneNumber)" case .url: historyItem.content = "URL: \(urlString)" default: historyItem.content = content } do { try context.save() alertMessage = "二维码创建成功!" showingAlert = true // 创建成功后返回 DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { dismiss() } } catch { alertMessage = "保存失败:\(error.localizedDescription)" showingAlert = true } } } #Preview { NavigationView { CreateQRCodeView(selectedQRCodeType: .mail) } }