import SwiftUI import CoreData import QRCode internal import SwiftImageReadWrite struct QRCodeDetailView: View { let historyItem: HistoryItem @EnvironmentObject var coreDataManager: CoreDataManager @State private var qrCodeImage: UIImage? @State private var showingShareSheet = false @State private var showingAlert = false @State private var alertMessage = "" @State private var navigateToStyleView = false var body: some View { ScrollView { VStack(spacing: 20) { // 二维码图片 qrCodeStyleSection // 解析后的详细信息 parsedInfoSection // 原始内容 originalContentSection // 操作按钮 actionButtonsSection // Decorate code按钮 decorateCodeButton } .padding() } .navigationTitle(getNavigationTitle()) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button(action: { showingShareSheet = true }) { Image(systemName: "square.and.arrow.up") } } } .onAppear { generateQRCodeImage() } .sheet(isPresented: $showingShareSheet) { ShareSheet(activityItems: [historyItem.content ?? ""]) } .alert("tip".localized, isPresented: $showingAlert) { Button("confirm".localized) { } } message: { Text(alertMessage) } .background( NavigationLink( destination: QRCodeStyleView( qrCodeContent: historyItem.content ?? "", qrCodeType: getQRCodeType(), existingStyleData: getStyleData(), historyItem: historyItem ), isActive: $navigateToStyleView ) { EmptyView() } ) } // MARK: - 二维码图片视图 private var qrCodeImageView: some View { VStack(spacing: 16) { if let qrCodeImage = qrCodeImage { Image(uiImage: qrCodeImage) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 200, height: 200) .cornerRadius(12) .shadow(radius: 8) } else { RoundedRectangle(cornerRadius: 12) .fill(Color.gray.opacity(0.3)) .frame(width: 200, height: 200) .overlay( ProgressView() .scaleEffect(1.5) ) } Text("scan_this_qr_code".localized) .font(.caption) .foregroundColor(.secondary) } } // MARK: - 解析后的详细信息 private var parsedInfoSection: some View { VStack(alignment: .leading, spacing: 12) { HStack { Image(systemName: "info.circle") .font(.title2) .foregroundColor(.green) Text("parsed_info".localized) .font(.headline) Spacer() } if let content = historyItem.content { let parsedData = QRCodeParser.parseQRCode(content) VStack(alignment: .leading, spacing: 8) { HStack { Image(systemName: parsedData.icon) .font(.title3) .foregroundColor(.green) Text(parsedData.title) .font(.title3) .fontWeight(.medium) Spacer() } if let subtitle = parsedData.subtitle { Text(subtitle) .font(.body) .foregroundColor(.secondary) .multilineTextAlignment(.leading) } } .padding() .background(Color.green.opacity(0.1)) .cornerRadius(8) } } .padding() .background(Color(.systemBackground)) .cornerRadius(12) .shadow(radius: 2) } // MARK: - 原始内容 private var originalContentSection: some View { VStack(alignment: .leading, spacing: 12) { HStack { Image(systemName: "doc.text") .font(.title2) .foregroundColor(.purple) Text("original_content".localized) .font(.headline) Spacer() } if let content = historyItem.content { ScrollView { Text(content) .font(.system(.body, design: .monospaced)) .foregroundColor(.secondary) .multilineTextAlignment(.leading) .padding() .frame(maxWidth: .infinity, alignment: .leading) } .frame(maxHeight: 200) .background(Color.purple.opacity(0.1)) .cornerRadius(8) } } .padding() .background(Color(.systemBackground)) .cornerRadius(12) .shadow(radius: 2) } // MARK: - 二维码样式信息 private var qrCodeStyleSection: some View { VStack(spacing: 0) { if let styleData = getStyleData() { // 使用样式生成二维码预览 VStack(spacing: 16) { // 样式预览二维码 if let previewImage = generateStylePreviewImage(styleData: styleData) { Image(uiImage: previewImage) .resizable() .aspectRatio(contentMode: .fit) .frame(maxWidth: 200, maxHeight: 200) .padding() .background(Color.white) .cornerRadius(12) .shadow(radius: 4) } // 样式标签 HStack(spacing: 8) { Label("custom".localized, systemImage: "paintpalette") .font(.caption) .padding(.horizontal, 8) .padding(.vertical, 4) .background(Color.purple.opacity(0.2)) .foregroundColor(.purple) .cornerRadius(6) } } .padding() .background(Color.purple.opacity(0.05)) .cornerRadius(12) } else { // 标准样式预览 VStack(spacing: 16) { if let standardImage = generateStandardQRCodeImage() { Image(uiImage: standardImage) .resizable() .aspectRatio(contentMode: .fit) .frame(maxWidth: 200, maxHeight: 200) .padding() .background(Color.white) .cornerRadius(12) .shadow(radius: 4) } Label("standard".localized, systemImage: "qrcode") .font(.caption) .padding(.horizontal, 8) .padding(.vertical, 4) .background(Color.gray.opacity(0.2)) .foregroundColor(.gray) .cornerRadius(6) } .padding() .background(Color.gray.opacity(0.05)) .cornerRadius(12) } } .padding() } // MARK: - 操作按钮 private var actionButtonsSection: some View { VStack(spacing: 12) { // 收藏按钮 Button(action: toggleFavorite) { HStack { Image(systemName: historyItem.isFavorite ? "heart.fill" : "heart") .foregroundColor(historyItem.isFavorite ? .red : .gray) Text(historyItem.isFavorite ? "unfavorite".localized : "favorite".localized) .fontWeight(.medium) } .frame(maxWidth: .infinity) .padding() .background(historyItem.isFavorite ? Color.red.opacity(0.1) : Color.gray.opacity(0.1)) .foregroundColor(historyItem.isFavorite ? .red : .gray) .cornerRadius(10) } // 复制内容按钮 Button(action: copyContent) { HStack { Image(systemName: "doc.on.doc") .foregroundColor(.blue) Text("copy_content".localized) .fontWeight(.medium) } .frame(maxWidth: .infinity) .padding() .background(Color.blue.opacity(0.1)) .foregroundColor(.blue) .cornerRadius(10) } // 打开链接按钮(如果是URL类型) if let content = historyItem.content, canOpenURL(content) { Button(action: { openURL(content) }) { HStack { Image(systemName: "arrow.up.right.square") .foregroundColor(.green) Text("open_link".localized) .fontWeight(.medium) } .frame(maxWidth: .infinity) .padding() .background(Color.green.opacity(0.1)) .foregroundColor(.green) .cornerRadius(10) } } } .padding() .background(Color(.systemBackground)) .cornerRadius(12) .shadow(radius: 2) } // MARK: - 生成二维码图片 private func generateQRCodeImage() { guard let content = historyItem.content else { return } do { let imageData = try QRCode.build .text(content) .quietZonePixelCount(3) .foregroundColor(CGColor(srgbRed: 1, green: 0, blue: 0.6, alpha: 1)) .backgroundColor(CGColor(srgbRed: 0, green: 0, blue: 0.2, alpha: 1)) .background.cornerRadius(3) .onPixels.shape(QRCode.PixelShape.CurvePixel()) .eye.shape(QRCode.EyeShape.Teardrop()) .generate.image(dimension: 600, representation: .png()) self.qrCodeImage = UIImage(data: imageData) } catch { print("生成二维码失败: \(error)") } } // MARK: - 切换收藏状态 private func toggleFavorite() { historyItem.isFavorite.toggle() coreDataManager.save() let message = historyItem.isFavorite ? "added_to_favorites".localized : "removed_from_favorites".localized alertMessage = message showingAlert = true } // MARK: - 复制内容 private func copyContent() { if let content = historyItem.content { UIPasteboard.general.string = content alertMessage = "content_copied_to_clipboard".localized showingAlert = true } } // MARK: - 检查是否可以打开URL private func canOpenURL(_ string: String) -> Bool { guard let url = URL(string: string) else { return false } return UIApplication.shared.canOpenURL(url) } // MARK: - 打开URL private func openURL(_ string: String) { guard let url = URL(string: string) else { return } UIApplication.shared.open(url) } } // MARK: - 分享表单 struct ShareSheet: UIViewControllerRepresentable { let activityItems: [Any] func makeUIViewController(context: Context) -> UIActivityViewController { let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: nil) return controller } func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {} } #Preview("Wi‑Fi") { let ctx = PreviewData.context let item = PreviewData.wifiSample(in: ctx) NavigationView { QRCodeDetailView(historyItem: item) } } #Preview("URL") { let ctx = PreviewData.context let item = PreviewData.urlSample(in: ctx) NavigationView { QRCodeDetailView(historyItem: item) } } #Preview("SMS") { let ctx = PreviewData.context let item = PreviewData.smsSample(in: ctx) NavigationView { QRCodeDetailView(historyItem: item) } } #Preview("vCard") { let ctx = PreviewData.context let item = PreviewData.vcardSample(in: ctx) NavigationView { QRCodeDetailView(historyItem: item) } } #Preview("Instagram") { let ctx = PreviewData.context let item = PreviewData.instagramSample(in: ctx) NavigationView { QRCodeDetailView(historyItem: item) } } #Preview("WhatsApp") { let ctx = PreviewData.context let item = PreviewData.whatsappSample(in: ctx) NavigationView { QRCodeDetailView(historyItem: item) } } #Preview("Viber") { let ctx = PreviewData.context let item = PreviewData.viberSample(in: ctx) NavigationView { QRCodeDetailView(historyItem: item) } } #Preview("Text") { let ctx = PreviewData.context let item = PreviewData.textSample(in: ctx) NavigationView { QRCodeDetailView(historyItem: item) } } #Preview("MeCard") { let ctx = PreviewData.context let item = PreviewData.mecardSample(in: ctx) NavigationView { QRCodeDetailView(historyItem: item) } } // MARK: - Preview Data private enum PreviewData { static let context: NSManagedObjectContext = { let container = NSPersistentContainer(name: "MyQrCode") let description = NSPersistentStoreDescription() description.type = NSInMemoryStoreType container.persistentStoreDescriptions = [description] container.loadPersistentStores { _, _ in } return container.viewContext }() private static func makeBaseItem(in context: NSManagedObjectContext, content: String, qrType: QRCodeType, favorite: Bool = false) -> HistoryItem { let item = HistoryItem(context: context) item.id = UUID() item.content = content item.dataType = DataType.qrcode.rawValue item.dataSource = DataSource.created.rawValue item.createdAt = Date() item.isFavorite = favorite item.qrCodeType = qrType.rawValue return item } static func wifiSample(in context: NSManagedObjectContext) -> HistoryItem { let content = "WIFI:T:WPA;S:MyNetwork;P:MyPassword;;" return makeBaseItem(in: context, content: content, qrType: .wifi, favorite: true) } static func urlSample(in context: NSManagedObjectContext) -> HistoryItem { let content = "https://www.example.com" return makeBaseItem(in: context, content: content, qrType: .url) } static func smsSample(in context: NSManagedObjectContext) -> HistoryItem { let content = "SMSTO:+1 (555) 123-4567:Hello" return makeBaseItem(in: context, content: content, qrType: .sms) } static func vcardSample(in context: NSManagedObjectContext) -> HistoryItem { let content = """ BEGIN:VCARD VERSION:3.0 N:Doe;John;;; FN:John Doe TEL;TYPE=WORK,CELL:(123) 456-7890 EMAIL;TYPE=PREF,INTERNET:john.doe@example.com ORG:Example Company TITLE:Software Engineer ADR;TYPE=WORK:;;123 Main St;Anytown;CA;12345;USA URL:https://example.com END:VCARD """.trimmingCharacters(in: .whitespacesAndNewlines) return makeBaseItem(in: context, content: content, qrType: .vcard) } static func instagramSample(in context: NSManagedObjectContext) -> HistoryItem { let content = "instagram://user?username=example_user" return makeBaseItem(in: context, content: content, qrType: .instagram) } static func whatsappSample(in context: NSManagedObjectContext) -> HistoryItem { let content = "whatsapp://send?phone=+1234567890" return makeBaseItem(in: context, content: content, qrType: .whatsapp) } static func textSample(in context: NSManagedObjectContext) -> HistoryItem { let content = "Hello, this is a text message!" return makeBaseItem(in: context, content: content, qrType: .text) } static func viberSample(in context: NSManagedObjectContext) -> HistoryItem { let content = "viber://add?number=+1234567890" return makeBaseItem(in: context, content: content, qrType: .viber) } static func mecardSample(in context: NSManagedObjectContext) -> HistoryItem { let content = "MECARD:N:Doe,John;NICKNAME:Johnny;TEL:+1234567890;EMAIL:john.doe@example.com;ORG:Example Company;TITLE:Software Engineer;ADR:123 Main St,Anytown,CA,12345,USA;URL:https://example.com;NOTE:This is a note;" return makeBaseItem(in: context, content: content, qrType: .mecard) } } // MARK: - 样式信息辅助方法 extension QRCodeDetailView { // MARK: - 生成样式预览图片 private func generateStylePreviewImage(styleData: QRCodeStyleData) -> UIImage? { guard let content = historyItem.content else { return nil } do { var qrCodeBuilder = try QRCode.build .text(content) .quietZonePixelCount(0) // 设置前景色 if let foregroundColor = getColorFromString(styleData.foregroundColor) { qrCodeBuilder = qrCodeBuilder.foregroundColor(foregroundColor) } // 设置背景色 if let backgroundColor = getColorFromString(styleData.backgroundColor) { qrCodeBuilder = qrCodeBuilder.backgroundColor(backgroundColor) } // 设置背景圆角 qrCodeBuilder = qrCodeBuilder.background.cornerRadius(3) // 设置点类型 if let dotType = getDotTypeFromString(styleData.dotType) { qrCodeBuilder = qrCodeBuilder.onPixels.shape(dotType) } // 设置眼睛类型 if let eyeType = getEyeTypeFromString(styleData.eyeType) { qrCodeBuilder = qrCodeBuilder.eye.shape(eyeType) } // 设置Logo(如果有的话) if let logo = styleData.logo, !logo.isEmpty { if let logoTemplate = getLogoFromString(logo) { qrCodeBuilder = qrCodeBuilder.logo(logoTemplate) } } // 生成图片 let imageData = try qrCodeBuilder.generate.image(dimension: 300, representation: .png()) return UIImage(data: imageData) } catch { print("生成样式预览图片失败: \(error)") return nil } } // MARK: - 生成标准二维码图片 private func generateStandardQRCodeImage() -> UIImage? { guard let content = historyItem.content else { return nil } do { let imageData = try QRCode.build .text(content) .quietZonePixelCount(0) .foregroundColor(CGColor(srgbRed: 0, green: 0, blue: 0, alpha: 1)) .backgroundColor(CGColor(srgbRed: 1, green: 1, blue: 1, alpha: 1)) .generate.image(dimension: 300, representation: .png()) return UIImage(data: imageData) } catch { print("生成标准二维码图片失败: \(error)") return nil } } // MARK: - 颜色转换 private func getColorFromString(_ colorString: String) -> CGColor? { if let color = QRCodeColor(rawValue: colorString) { return color.cgColor } return nil } // MARK: - 点类型转换 private func getDotTypeFromString(_ dotTypeString: String) -> QRCodePixelShapeGenerator? { if let dotType = QRCodeDotType(rawValue: dotTypeString) { return dotType.pixelShape } return nil } // MARK: - 眼睛类型转换 private func getEyeTypeFromString(_ eyeTypeString: String) -> QRCodeEyeShapeGenerator? { if let eyeType = QRCodeEyeType(rawValue: eyeTypeString) { return eyeType.eyeShape } return nil } // MARK: - Logo转换 private func getLogoFromString(_ logoString: String) -> QRCode.LogoTemplate? { // 检查是否是自定义Logo if logoString.hasPrefix("custom_") { // 从样式数据中获取自定义Logo图片 if let styleData = getStyleData(), let customImage = styleData.customLogoImage, let cgImage = customImage.cgImage { return QRCode.LogoTemplate.CircleCenter(image: cgImage, inset: 0) } } else { // 预设Logo if let logo = QRCodeLogo(rawValue: logoString), let image = logo.image, let cgImage = image.cgImage { return QRCode.LogoTemplate.CircleCenter(image: cgImage) } } return nil } // MARK: - 从JSON字符串解析样式数据 private func getStyleData() -> QRCodeStyleData? { guard let jsonString = historyItem.qrCodeStyleData, let jsonData = jsonString.data(using: .utf8) else { return nil } do { let styleData = try JSONDecoder().decode(QRCodeStyleData.self, from: jsonData) return styleData } catch { print("❌ 样式数据JSON解码失败:\(error)") return nil } } // MARK: - 显示名称转换方法(保留原有方法) private func getColorDisplayName(_ colorString: String) -> String { if let color = QRCodeColor(rawValue: colorString) { switch color { case .black: return "black".localized case .white: return "white".localized case .red: return "red".localized case .blue: return "blue".localized case .green: return "green".localized case .yellow: return "yellow".localized case .purple: return "purple".localized case .orange: return "orange".localized case .pink: return "pink".localized case .cyan: return "cyan".localized case .magenta: return "magenta".localized case .brown: return "brown".localized case .gray: return "gray".localized case .navy: return "navy".localized case .teal: return "teal".localized case .indigo: return "indigo".localized case .lime: return "lime".localized case .maroon: return "maroon".localized case .olive: return "olive".localized case .silver: return "silver".localized } } return colorString } private func getDotTypeDisplayName(_ dotTypeString: String) -> String { if let dotType = QRCodeDotType(rawValue: dotTypeString) { return dotType.displayName } return dotTypeString } private func getEyeTypeDisplayName(_ eyeTypeString: String) -> String { if let eyeType = QRCodeEyeType(rawValue: eyeTypeString) { return eyeType.displayName } return eyeTypeString } private func getLogoDisplayName(_ logoString: String) -> String { if let logo = QRCodeLogo(rawValue: logoString) { return logo.displayName } return logoString } // MARK: - 获取二维码类型 private func getQRCodeType() -> QRCodeType { if let qrCodeTypeString = historyItem.qrCodeType, let qrCodeType = QRCodeType(rawValue: qrCodeTypeString) { return qrCodeType } return .text // 默认返回text类型 } // MARK: - 获取导航标题 private func getNavigationTitle() -> String { if let qrCodeTypeString = historyItem.qrCodeType, let qrCodeType = QRCodeType(rawValue: qrCodeTypeString) { return qrCodeType.displayName } return "qr_code_detail".localized } // MARK: - Decorate code按钮 private var decorateCodeButton: some View { VStack(spacing: 16) { Button(action: { navigateToCustomStyle() }) { HStack(spacing: 12) { Image(systemName: "paintpalette.fill") .font(.title2) .foregroundColor(.white) Text("decorate_code".localized) .font(.headline) .fontWeight(.semibold) .foregroundColor(.white) Spacer() Image(systemName: "chevron.right") .font(.system(size: 14, weight: .medium)) .foregroundColor(.white.opacity(0.8)) } .padding(.horizontal, 20) .padding(.vertical, 16) .background( LinearGradient( gradient: Gradient(colors: [Color.purple, Color.blue]), startPoint: .leading, endPoint: .trailing ) ) .cornerRadius(12) .shadow(color: .purple.opacity(0.3), radius: 8, x: 0, y: 4) } .buttonStyle(PlainButtonStyle()) // 如果有现有样式,显示提示 if getStyleData() != nil { HStack { Image(systemName: "info.circle.fill") .font(.caption) .foregroundColor(.orange) Text("qr_code_has_style".localized) .font(.caption) .foregroundColor(.secondary) Spacer() } .padding(.horizontal, 4) } } .padding() .background(Color(.systemBackground)) .cornerRadius(12) .shadow(radius: 2) } // MARK: - 跳转到自定义样式界面 private func navigateToCustomStyle() { navigateToStyleView = true } }