diff --git a/MyQrCode/Views/CreateQRCodeView.swift b/MyQrCode/Views/CreateQRCodeView.swift index 8ba7c72..93241f7 100644 --- a/MyQrCode/Views/CreateQRCodeView.swift +++ b/MyQrCode/Views/CreateQRCodeView.swift @@ -17,6 +17,19 @@ struct CreateQRCodeView: View { // 输入焦点,确保进入页面自动弹出键盘 @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: EmailField? + + // Email字段枚举 + private enum EmailField: Hashable { + case address, subject, body, cc, bcc + } + var body: some View { VStack(spacing: 0) { inputAndPreviewSection @@ -26,7 +39,7 @@ struct CreateQRCodeView: View { .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("创建") { createQRCode() } - .disabled(content.isEmpty) + .disabled(!canCreateQRCode()) .font(.system(size: 16, weight: .semibold)) } } @@ -36,18 +49,159 @@ struct CreateQRCodeView: View { .onAppear { // 稍延迟以确保进入页面时自动聚焦 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - isContentFieldFocused = true + if selectedQRCodeType == .mail { + focusedEmailField = .address + } else { + isContentFieldFocused = true + } } } .onTapGesture { // 点击外部关闭键盘 - isContentFieldFocused = false + if selectedQRCodeType == .mail { + focusedEmailField = nil + } else { + isContentFieldFocused = false + } } } // MARK: - UI Components - + private var emailInputSection: some View { + VStack(spacing: 16) { + // Email地址 (必填) + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("邮箱地址") + .font(.subheadline) + .foregroundColor(.primary) + Text("*") + .foregroundColor(.red) + Spacer() + } + + TextField("user@example.com", text: $emailAddress) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .keyboardType(.emailAddress) + .autocapitalization(.none) + .focused($focusedEmailField, equals: .address) + } + + // 主题 (必填) + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("主题") + .font(.subheadline) + .foregroundColor(.primary) + Text("*") + .foregroundColor(.red) + Spacer() + } + + TextField("邮件主题", text: $emailSubject) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .focused($focusedEmailField, equals: .subject) + } + + // 正文 (必填) + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("正文") + .font(.subheadline) + .foregroundColor(.primary) + Text("*") + .foregroundColor(.red) + Spacer() + } + + ZStack { + TextEditor(text: $emailBody) + .frame(minHeight: 120) + .padding(8) + .background(Color(.systemBackground)) + .cornerRadius(8) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(focusedEmailField == .body ? Color.blue : Color(.systemGray4), lineWidth: 1) + ) + .focused($focusedEmailField, equals: .body) + .onChange(of: emailBody) { newValue in + // 限制最大字符数为1200 + if newValue.count > 1200 { + emailBody = String(newValue.prefix(1200)) + } + } + + // 占位符文本 + if emailBody.isEmpty && focusedEmailField != .body { + VStack { + HStack { + Text("输入邮件正文内容...") + .foregroundColor(.secondary) + .font(.body) + Spacer() + } + Spacer() + } + .padding(16) + .allowsHitTesting(false) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + } + } + + // 字符计数 + HStack { + Spacer() + Text("\(emailBody.count)/1200") + .font(.caption) + .foregroundColor(emailBody.count >= 1200 ? .orange : .secondary) + } + } + + // CC地址 (可选) + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("抄送地址") + .font(.subheadline) + .foregroundColor(.primary) + Spacer() + } + + TextField("cc@example.com", text: $emailCc) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .keyboardType(.emailAddress) + .autocapitalization(.none) + .focused($focusedEmailField, equals: .cc) + } + + // BCC地址 (可选) + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("密送地址") + .font(.subheadline) + .foregroundColor(.primary) + Spacer() + } + + TextField("bcc@example.com", text: $emailBcc) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .keyboardType(.emailAddress) + .autocapitalization(.none) + .focused($focusedEmailField, equals: .bcc) + } + } + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + Spacer() + Button("完成") { + focusedEmailField = nil + } + .foregroundColor(.blue) + .font(.system(size: 16, weight: .medium)) + } + } + } private var inputAndPreviewSection: some View { ScrollView { @@ -78,93 +232,96 @@ struct CreateQRCodeView: View { // 内容输入区域 VStack(spacing: 16) { HStack { - Text("输入内容") + Text(selectedQRCodeType == .mail ? "邮件信息" : "输入内容") .font(.headline) .foregroundColor(.primary) Spacer() } - // 多行输入框 - VStack(spacing: 8) { - ZStack { - // 输入框主体 - TextEditor(text: $content) - .frame(minHeight: 120) - .padding(8) - .background(Color(.systemBackground)) - .cornerRadius(8) - .overlay( - RoundedRectangle(cornerRadius: 8) - .stroke(isContentFieldFocused ? Color.blue : Color(.systemGray4), lineWidth: 1) - ) - .focused($isContentFieldFocused) - .onChange(of: content) { newValue in - // 限制最大字符数为150 - if newValue.count > 150 { - content = String(newValue.prefix(150)) + if selectedQRCodeType == .mail { + // Email专用输入界面 + emailInputSection + } else { + // 通用输入界面 + VStack(spacing: 8) { + ZStack { + // 输入框主体 + TextEditor(text: $content) + .frame(minHeight: 120) + .padding(8) + .background(Color(.systemBackground)) + .cornerRadius(8) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(isContentFieldFocused ? Color.blue : Color(.systemGray4), lineWidth: 1) + ) + .focused($isContentFieldFocused) + .onChange(of: content) { newValue in + // 限制最大字符数为150 + if newValue.count > 150 { + content = String(newValue.prefix(150)) + } } - } - .toolbar { - ToolbarItemGroup(placement: .keyboard) { - Spacer() - Button("完成") { - isContentFieldFocused = false + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + Spacer() + Button("完成") { + isContentFieldFocused = false + } + .foregroundColor(.blue) + .font(.system(size: 16, weight: .medium)) } - .foregroundColor(.blue) - .font(.system(size: 16, weight: .medium)) } - } - - // 占位符文本 - 左上角对齐 - if content.isEmpty && !isContentFieldFocused { - VStack { - HStack { - Text(getPlaceholderText()) - .foregroundColor(.secondary) - .font(.body) + + // 占位符文本 - 左上角对齐 + if content.isEmpty && !isContentFieldFocused { + VStack { + HStack { + Text(getPlaceholderText()) + .foregroundColor(.secondary) + .font(.body) + Spacer() + } Spacer() } - Spacer() + .padding(16) + .allowsHitTesting(false) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) } - .padding(16) - .allowsHitTesting(false) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) } - } - - // 字符计数和限制提示 - 输入框底下 - HStack { - Spacer() - - VStack(alignment: .trailing, spacing: 4) { - // 字符限制提示 - if content.count >= 150 { - HStack(spacing: 4) { - Image(systemName: "exclamationmark.triangle") - .font(.caption) - .foregroundColor(.orange) - - Text("已达到最大字符数") - .font(.caption) - .foregroundColor(.orange) - } - } else if content.count >= 140 { - HStack(spacing: 4) { - Image(systemName: "info.circle") - .font(.caption) - .foregroundColor(.blue) - - Text("接近字符限制") - .font(.caption) - .foregroundColor(.blue) + + // 字符计数和限制提示 - 输入框底下 + HStack { + Spacer() // Pushes content to the right + + VStack(alignment: .trailing, spacing: 4) { + // 字符限制提示 + if content.count >= 150 { + HStack(spacing: 4) { + Image(systemName: "exclamationmark.triangle") + .font(.caption) + .foregroundColor(.orange) + Text("已达到最大字符数") + .font(.caption) + .foregroundColor(.orange) + } + } else if content.count >= 140 { + HStack(spacing: 4) { + Image(systemName: "info.circle") + .font(.caption) + .foregroundColor(.blue) + Text("接近字符限制") + .font(.caption) + .foregroundColor(.blue) + } } + + // 字符计数 + Text("\(content.count)/150") + .font(.caption) + .foregroundColor(getCharacterCountColor()) } - - // 字符计数 - Text("\(content.count)/150") - .font(.caption) - .foregroundColor(getCharacterCountColor()) } } } @@ -172,7 +329,7 @@ struct CreateQRCodeView: View { .padding(.horizontal, 20) // 预览区域 - if !content.isEmpty { + if selectedQRCodeType == .mail ? (!emailAddress.isEmpty && !emailSubject.isEmpty && !emailBody.isEmpty) : !content.isEmpty { VStack(spacing: 16) { HStack { Text("预览") @@ -180,20 +337,13 @@ struct CreateQRCodeView: View { .foregroundColor(.primary) Spacer() - - Button(action: { - // 可以添加分享功能 - }) { - Image(systemName: "square.and.arrow.up") - .font(.system(size: 16)) - .foregroundColor(.blue) - } } + // 二维码预览卡片 VStack(spacing: 16) { // 二维码图片 - if let qrCodeImage = generateQRCodeImage() { - Image(uiImage: qrCodeImage) + if let qrImage = generateQRCodeImage() { + Image(uiImage: qrImage) .interpolation(.none) .resizable() .scaledToFit() @@ -245,10 +395,19 @@ struct CreateQRCodeView: View { .background(Color(.systemGroupedBackground)) } - - // MARK: - Helper Methods + private func canCreateQRCode() -> Bool { + switch selectedQRCodeType { + case .mail: + // Email类型:邮箱地址、主题、正文为必填 + return !emailAddress.isEmpty && !emailSubject.isEmpty && !emailBody.isEmpty + default: + // 其他类型:内容不能为空 + return !content.isEmpty + } + } + private func getCharacterCountColor() -> Color { if content.count >= 150 { return .orange @@ -266,7 +425,7 @@ struct CreateQRCodeView: View { case .url: return "输入网址,如:https://www.example.com" case .mail: - return "输入邮箱地址,如:user@example.com" + return "填写邮件信息,邮箱地址、主题、正文为必填项" case .phone: return "输入电话号码,如:+86 138 0013 8000" case .sms: @@ -303,46 +462,55 @@ struct CreateQRCodeView: View { private func getPlaceholderText() -> String { switch selectedQRCodeType { case .text: - return "输入文本内容" + return "输入任意文本内容..." case .url: - return "输入网址" + return "输入网址..." case .mail: - return "输入邮箱地址" + return "输入邮件内容..." case .phone: - return "输入电话号码" + return "输入电话号码..." case .sms: - return "输入短信内容" + return "输入短信内容..." case .wifi: - return "输入WiFi信息" + return "输入WiFi信息..." case .vcard: - return "输入联系人信息" + return "输入联系人信息..." case .mecard: - return "输入联系人信息" + return "输入联系人信息..." case .location: - return "输入地理位置坐标" + return "输入地理位置..." case .calendar: - return "输入日历事件信息" + return "输入日历事件信息..." case .instagram: - return "输入Instagram信息" + return "输入Instagram信息..." case .facebook: - return "输入Facebook信息" + return "输入Facebook信息..." case .spotify: - return "输入Spotify链接" + return "输入Spotify信息..." case .twitter: - return "输入Twitter信息" + return "输入Twitter信息..." case .whatsapp: - return "输入WhatsApp消息" + return "输入WhatsApp信息..." case .viber: - return "输入Viber消息" + return "输入Viber信息..." case .snapchat: - return "输入Snapchat用户名" + return "输入Snapchat信息..." case .tiktok: - return "输入TikTok信息" + return "输入TikTok信息..." } } private func generateQRCodeImage() -> UIImage? { - guard !content.isEmpty else { return nil } + // 根据二维码类型检查内容是否为空 + let hasContent: Bool + switch selectedQRCodeType { + case .mail: + hasContent = !emailAddress.isEmpty && !emailSubject.isEmpty && !emailBody.isEmpty + default: + hasContent = !content.isEmpty + } + + guard hasContent else { return nil } // 根据二维码类型格式化内容 let formattedContent = formatContentForQRCodeType() @@ -369,7 +537,30 @@ struct CreateQRCodeView: View { case .url: return content.hasPrefix("http") ? content : "https://\(content)" case .mail: - return "mailto:\(content)" + 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:\(content)" case .sms: @@ -404,18 +595,30 @@ struct CreateQRCodeView: View { } private func createQRCode() { - guard !content.isEmpty else { return } - let context = coreDataManager.container.viewContext let historyItem = HistoryItem(context: context) historyItem.id = UUID() - historyItem.content = content 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 + default: + historyItem.content = content + } + do { try context.save() alertMessage = "二维码创建成功!" @@ -433,6 +636,6 @@ struct CreateQRCodeView: View { #Preview { NavigationView { - CreateQRCodeView(selectedQRCodeType: .text) + CreateQRCodeView(selectedQRCodeType: .mail) } -} \ No newline at end of file +} diff --git a/MyQrCode/Views/CreateQRCodeView.swift.backup b/MyQrCode/Views/CreateQRCodeView.swift.backup new file mode 100644 index 0000000..3d39b95 --- /dev/null +++ b/MyQrCode/Views/CreateQRCodeView.swift.backup @@ -0,0 +1,642 @@ +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 = "" + @State private var showingAlert = false + @State private var alertMessage = "" + + // 输入焦点,确保进入页面自动弹出键盘 + @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: EmailField? + + // Email字段枚举 + private enum EmailField: Hashable { + case address, subject, body, cc, bcc + } + + 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 { + // 稍延迟以确保进入页面时自动聚焦 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + isContentFieldFocused = true + } + } + .onTapGesture { + // 点击外部关闭键盘 + isContentFieldFocused = false + } + } + + // MARK: - UI Components + + private var emailInputSection: some View { + VStack(spacing: 16) { + // Email地址 (必填) + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("邮箱地址") + .font(.subheadline) + .foregroundColor(.primary) + Text("*") + .foregroundColor(.red) + Spacer() + } + + TextField("user@example.com", text: $emailAddress) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .keyboardType(.emailAddress) + .autocapitalization(.none) + .focused($focusedEmailField, equals: .address) + } + + // 主题 (必填) + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("主题") + .font(.subheadline) + .foregroundColor(.primary) + Text("*") + .foregroundColor(.red) + Spacer() + } + + TextField("邮件主题", text: $emailSubject) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .focused($focusedEmailField, equals: .subject) + } + + // 正文 (必填) + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("正文") + .font(.subheadline) + .foregroundColor(.primary) + Text("*") + .foregroundColor(.red) + Spacer() + } + + ZStack { + TextEditor(text: $emailBody) + .frame(minHeight: 120) + .padding(8) + .background(Color(.systemBackground)) + .cornerRadius(8) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(focusedEmailField == .body ? Color.blue : Color(.systemGray4), lineWidth: 1) + ) + .focused($focusedEmailField, equals: .body) + .onChange(of: emailBody) { newValue in + // 限制最大字符数为1200 + if newValue.count > 1200 { + emailBody = String(newValue.prefix(1200)) + } + } + + // 占位符文本 + if emailBody.isEmpty && focusedEmailField != .body { + VStack { + HStack { + Text("输入邮件正文内容...") + .foregroundColor(.secondary) + .font(.body) + Spacer() + } + Spacer() + } + .padding(16) + .allowsHitTesting(false) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + } + } + + // 字符计数 + HStack { + Spacer() + Text("\(emailBody.count)/1200") + .font(.caption) + .foregroundColor(emailBody.count >= 1200 ? .orange : .secondary) + } + } + + // CC地址 (可选) + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("抄送地址") + .font(.subheadline) + .foregroundColor(.primary) + Spacer() + } + + TextField("cc@example.com", text: $emailCc) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .keyboardType(.emailAddress) + .autocapitalization(.none) + .focused($focusedEmailField, equals: .cc) + } + + // BCC地址 (可选) + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("密送地址") + .font(.subheadline) + .foregroundColor(.primary) + Spacer() + } + + TextField("bcc@example.com", text: $emailBcc) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .keyboardType(.emailAddress) + .autocapitalization(.none) + .focused($focusedEmailField, equals: .bcc) + } + } + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + Spacer() + Button("完成") { + focusedEmailField = nil + } + .foregroundColor(.blue) + .font(.system(size: 16, weight: .medium)) + } + } + } + + private var inputAndPreviewSection: some View { + ScrollView { + VStack(spacing: 24) { + // 输入提示 + VStack(spacing: 12) { + HStack { + Image(systemName: "info.circle") + .font(.caption) + .foregroundColor(.blue) + + Text(getContentHint()) + .font(.caption) + .foregroundColor(.secondary) + .lineLimit(nil) + + Spacer() + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(Color.blue.opacity(0.1)) + ) + } + .padding(.horizontal, 20) + + // 内容输入区域 + VStack(spacing: 16) { + HStack { + Text(selectedQRCodeType == .mail ? "邮件信息" : "输入内容") + .font(.headline) + .foregroundColor(.primary) + + Spacer() + } + + if selectedQRCodeType == .mail { + // Email专用输入界面 + emailInputSection + } else { + // 通用输入界面 + VStack(spacing: 8) { + ZStack { + // 输入框主体 + TextEditor(text: $content) + .frame(minHeight: 120) + .padding(8) + .background(Color(.systemBackground)) + .cornerRadius(8) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(isContentFieldFocused ? Color.blue : Color(.systemGray4), lineWidth: 1) + ) + .focused($isContentFieldFocused) + .onChange(of: content) { newValue in + // 限制最大字符数为150 + if newValue.count > 150 { + content = String(newValue.prefix(150)) + } + } + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + Spacer() + Button("完成") { + isContentFieldFocused = false + } + .foregroundColor(.blue) + .font(.system(size: 16, weight: .medium)) + } + } + + // 占位符文本 - 左上角对齐 + if content.isEmpty && !isContentFieldFocused { + VStack { + HStack { + Text(getPlaceholderText()) + .foregroundColor(.secondary) + .font(.body) + Spacer() + } + Spacer() + } + .padding(16) + .allowsHitTesting(false) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + } + } + + // 字符计数和限制提示 - 输入框底下 + HStack { + Spacer() + + VStack(alignment: .trailing, spacing: 4) { + // 字符限制提示 + if content.count >= 150 { + HStack(spacing: 4) { + Image(systemName: "exclamationmark.triangle") + .font(.caption) + .foregroundColor(.orange) + + Text("已达到最大字符数") + .font(.caption) + .foregroundColor(.orange) + } + } else if content.count >= 140 { + HStack(spacing: 4) { + Image(systemName: "info.circle") + .font(.caption) + .foregroundColor(.blue) + + Text("接近字符限制") + .font(.caption) + .foregroundColor(.blue) + } + } + + // 字符计数 + Text("\(content.count)/150") + .font(.caption) + .foregroundColor(getCharacterCountColor()) + } + } + } + } + .padding(.horizontal, 20) + + // 预览区域 + if !content.isEmpty { + VStack(spacing: 16) { + HStack { + Text("预览") + .font(.headline) + .foregroundColor(.primary) + + Spacer() + + Button(action: { + // 可以添加分享功能 + }) { + Image(systemName: "square.and.arrow.up") + .font(.system(size: 16)) + .foregroundColor(.blue) + } + } + + VStack(spacing: 16) { + // 二维码图片 + if let qrCodeImage = generateQRCodeImage() { + Image(uiImage: qrCodeImage) + .interpolation(.none) + .resizable() + .scaledToFit() + .frame(width: 200, height: 200) + .background(Color.white) + .cornerRadius(12) + .shadow(color: .black.opacity(0.1), radius: 8, x: 0, y: 4) + } + + // 内容预览卡片 + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("内容") + .font(.caption) + .foregroundColor(.secondary) + + Spacer() + + Text(selectedQRCodeType.displayName) + .font(.caption) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background(Color.orange.opacity(0.1)) + .foregroundColor(.orange) + .cornerRadius(4) + } + + Text(formatContentForQRCodeType()) + .font(.body) + .foregroundColor(.primary) + .textSelection(.enabled) + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(8) + } + .padding() + .background(Color(.systemBackground)) + .cornerRadius(12) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 2) + } + .padding(.horizontal, 20) + } + + Spacer(minLength: 100) + } + .padding(.top, 20) + } + .background(Color(.systemGroupedBackground)) + } + + + + // MARK: - Helper Methods + + private func canCreateQRCode() -> Bool { + switch selectedQRCodeType { + case .mail: + // Email类型:邮箱地址、主题、正文为必填 + return !emailAddress.isEmpty && !emailSubject.isEmpty && !emailBody.isEmpty + default: + // 其他类型:内容不能为空 + return !content.isEmpty + } + } + + private func getCharacterCountColor() -> Color { + if content.count >= 150 { + return .orange + } else if content.count >= 140 { + return .blue + } else { + return .secondary + } + } + + private func getContentHint() -> String { + switch selectedQRCodeType { + case .text: + return "输入任意文本内容" + case .url: + return "输入网址,如:https://www.example.com" + case .mail: + return "输入邮箱地址,如:user@example.com" + case .phone: + return "输入电话号码,如:+86 138 0013 8000" + case .sms: + return "输入短信内容,如:Hello World" + case .wifi: + return "输入WiFi信息,如:SSID:MyWiFi,Password:12345678" + case .vcard: + return "输入联系人信息" + case .mecard: + return "输入联系人信息(简化版)" + case .location: + return "输入地理位置,如:40.7128,-74.0060" + case .calendar: + return "输入日历事件信息" + case .instagram: + return "输入Instagram用户名或链接" + case .facebook: + return "输入Facebook用户名或链接" + case .spotify: + return "输入Spotify歌曲或播放列表链接" + case .twitter: + return "输入Twitter用户名或链接" + case .whatsapp: + return "输入WhatsApp消息内容" + case .viber: + return "输入Viber消息内容" + case .snapchat: + return "输入Snapchat用户名" + case .tiktok: + return "输入TikTok用户名或链接" + } + } + + private func getPlaceholderText() -> String { + switch selectedQRCodeType { + case .text: + return "输入文本内容" + case .url: + return "输入网址" + case .mail: + return "输入邮箱地址" + case .phone: + return "输入电话号码" + case .sms: + return "输入短信内容" + case .wifi: + return "输入WiFi信息" + case .vcard: + return "输入联系人信息" + case .mecard: + return "输入联系人信息" + case .location: + return "输入地理位置坐标" + case .calendar: + return "输入日历事件信息" + case .instagram: + return "输入Instagram信息" + case .facebook: + return "输入Facebook信息" + case .spotify: + return "输入Spotify链接" + case .twitter: + return "输入Twitter信息" + case .whatsapp: + return "输入WhatsApp消息" + case .viber: + return "输入Viber消息" + case .snapchat: + return "输入Snapchat用户名" + case .tiktok: + return "输入TikTok信息" + } + } + + private func generateQRCodeImage() -> UIImage? { + // 根据二维码类型检查内容是否为空 + let hasContent: Bool + switch selectedQRCodeType { + case .mail: + hasContent = !emailAddress.isEmpty && !emailSubject.isEmpty && !emailBody.isEmpty + default: + hasContent = !content.isEmpty + } + + guard hasContent 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 } + + // 转换为UIImage + 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 content.hasPrefix("http") ? content : "https://\(content)" + 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:\(content)" + case .sms: + return "sms:\(content)" + case .wifi: + return "WIFI:T:WPA;S:\(content);P:password;;" + case .vcard: + return "BEGIN:VCARD\nVERSION:3.0\nFN:\(content)\nEND:VCARD" + case .mecard: + return "MECARD:N:\(content);;" + case .location: + return "geo:\(content)" + case .calendar: + return "BEGIN:VEVENT\nSUMMARY:\(content)\nEND:VEVENT" + case .instagram: + return "https://instagram.com/\(content)" + case .facebook: + return "https://facebook.com/\(content)" + case .spotify: + return content.hasPrefix("http") ? content : "https://open.spotify.com/track/\(content)" + case .twitter: + return "https://twitter.com/\(content)" + case .whatsapp: + return "https://wa.me/\(content)" + case .viber: + return "viber://chat?number=\(content)" + case .snapchat: + return "https://snapchat.com/add/\(content)" + case .tiktok: + return "https://tiktok.com/@\(content)" + } + } + + 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: + historyItem.content = "邮箱: \(emailAddress)\n主题: \(emailSubject)\n正文: \(emailBody)" + if !emailCc.isEmpty { + historyItem.content += "\n抄送: \(emailCc)" + } + if !emailBcc.isEmpty { + historyItem.content += "\n密送: \(emailBcc)" + } + 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: .text) + } +} \ No newline at end of file