import SwiftUI import CoreData struct CreateCodeView: View { @Environment(\.dismiss) private var dismiss @StateObject private var coreDataManager = CoreDataManager.shared @State private var selectedDataType: DataType = .qrcode @State private var selectedBarcodeType: BarcodeType = .ean13 @State private var selectedQRCodeType: QRCodeType = .text @State private var content = "" @State private var showingAlert = false @State private var alertMessage = "" // 条形码验证相关状态 @State private var validationResult: BarcodeValidator.ValidationResult? @State private var showValidationInfo = false // 输入焦点,确保进入页面自动弹出键盘 @FocusState private var isContentFieldFocused: Bool var body: some View { Form { // 数据类型选择 Section("数据类型") { HStack(spacing: 0) { ForEach(DataType.allCases, id: \.self) { type in Button(action: { selectedDataType = type isContentFieldFocused = true // 添加触觉反馈 let impactFeedback = UIImpactFeedbackGenerator(style: .light) impactFeedback.impactOccurred() }) { HStack(spacing: 6) { Image(systemName: type.icon) .font(.system(size: 16, weight: .medium)) .foregroundColor(selectedDataType == type ? .white : .primary) Text(type.displayName) .font(.system(size: 15, weight: .medium)) .foregroundColor(selectedDataType == type ? .white : .primary) } .frame(maxWidth: .infinity) .padding(.vertical, 12) .background( RoundedRectangle(cornerRadius: 8) .fill(selectedDataType == type ? Color.blue : Color(.systemGray6)) ) } .buttonStyle(PlainButtonStyle()) } } .background( RoundedRectangle(cornerRadius: 8) .fill(Color(.systemGray6)) ) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(Color(.systemGray4), lineWidth: 0.5) ) } // 具体类型选择 if selectedDataType == .barcode { Section("条形码类型") { Picker("条形码类型", selection: $selectedBarcodeType) { ForEach(BarcodeType.allCases, id: \.self) { type in HStack { Image(systemName: type.icon) Text(type.displayName) } .tag(type) } } .pickerStyle(WheelPickerStyle()) } } else { Section("二维码类型") { Picker("二维码类型", selection: $selectedQRCodeType) { ForEach(QRCodeType.allCases, id: \.self) { type in HStack { Image(systemName: type.icon) Text(type.displayName) } .tag(type) } } .pickerStyle(WheelPickerStyle()) } } // 内容输入 Section("内容") { VStack(alignment: .leading, spacing: 12) { // 输入框和计数布局 VStack(alignment: .trailing, spacing: 4) { // 条形码格式提示 if selectedDataType == .barcode { HStack { Image(systemName: "info.circle") .font(.caption) .foregroundColor(.blue) Text(getBarcodeFormatHint()) .font(.caption) .foregroundColor(.secondary) .lineLimit(2) Spacer() } .padding(.horizontal, 4) .padding(.vertical, 6) .background( RoundedRectangle(cornerRadius: 6) .fill(Color.blue.opacity(0.1)) ) } TextField(getPlaceholderText(), text: $content) .textFieldStyle(RoundedBorderTextFieldStyle()) .keyboardType(getKeyboardType()) .textInputAutocapitalization(getAutocapitalization()) .disableAutocorrection(true) .focused($isContentFieldFocused) .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("完成") { isContentFieldFocused = false } .foregroundColor(.blue) .font(.system(size: 16, weight: .medium)) } } .foregroundColor(.black) .onChange(of: content) { newValue in // 针对有固定长度与数字要求的类型,进行输入规范化(仅保留数字并按最大长度截断) if selectedDataType == .barcode, let maxLen = getMaxLength(), requiresDigitsOnly() { let digits = newValue.filter { $0.isNumber } let truncated = String(digits.prefix(maxLen)) if truncated != newValue { content = truncated // 播放输入提示音 playInputSound() } } else if selectedDataType == .barcode { // 播放输入提示音 playInputSound() } validateBarcodeContent(content) } // 计数显示在输入框下方,右对齐 if let maxLen = getMaxLength(), selectedDataType == .barcode { Text("\(normalizedInputCount())/\(maxLen)") .font(.caption2) .foregroundColor(.blue) .fontWeight(.medium) } } // 输入统计与验证状态 HStack { if let result = validationResult, result.isValid { Text("✓ 格式正确") .font(.caption2) .foregroundColor(.green) } else if !content.isEmpty { Text("⚠ 格式检查中...") .font(.caption2) .foregroundColor(.orange) } } // 条形码验证信息(仅在格式不正确时显示) if selectedDataType == .barcode && !content.isEmpty && (validationResult == nil || !validationResult!.isValid) { BarcodeValidationInfoView( validationResult: validationResult, barcodeType: selectedBarcodeType ) } } } // 预览 if !content.isEmpty { Section("预览") { VStack(alignment: .leading, spacing: 12) { HStack { Image(systemName: selectedDataType.icon) Text(selectedDataType.displayName) Spacer() if selectedDataType == .barcode { Text(selectedBarcodeType.displayName) .font(.caption) .padding(.horizontal, 8) .padding(.vertical, 2) .background(Color.green.opacity(0.1)) .foregroundColor(.green) .cornerRadius(8) } else { Text(selectedQRCodeType.displayName) .font(.caption) .padding(.horizontal, 8) .padding(.vertical, 2) .background(Color.orange.opacity(0.1)) .foregroundColor(.orange) .cornerRadius(8) } } if selectedDataType == .barcode && !content.isEmpty && isInputComplete() { // 条形码图片预览(仅在输入完成时显示) BarcodePreviewView( content: content, barcodeType: selectedBarcodeType ) .frame(height: 120) .frame(maxWidth: .infinity) } else { // 二维码或文本预览 Text(content) .font(.body) .foregroundColor(.secondary) .padding() .frame(maxWidth: .infinity, alignment: .leading) } } .padding() .background(Color(.systemGray6)) .cornerRadius(8) } } } .navigationTitle("创建\(selectedDataType.displayName)") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("创建") { createCode() } .disabled(content.isEmpty) } } .alert("提示", isPresented: $showingAlert) { Button("确定") { } } message: { Text(alertMessage) } .onAppear { // 稍延迟以确保进入页面时自动聚焦 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { isContentFieldFocused = true } } .onChange(of: selectedDataType) { _ in isContentFieldFocused = true } .onChange(of: selectedBarcodeType) { _ in isContentFieldFocused = true } .onTapGesture { // 点击外部关闭键盘 isContentFieldFocused = false } } // MARK: - 条形码验证方法 private func validateBarcodeContent(_ newContent: String) { guard selectedDataType == .barcode else { return } validationResult = BarcodeValidator.validateBarcode(newContent, type: selectedBarcodeType) // 如果验证通过,自动格式化内容 if let result = validationResult, result.isValid { content = result.formattedContent } } // MARK: - 键盘配置方法 private func getKeyboardType() -> UIKeyboardType { guard selectedDataType == .barcode else { return .default } switch selectedBarcodeType { case .ean13, .ean8, .upce, .itf14: return .numberPad case .code39, .code128, .pdf417: return .asciiCapable } } private func getAutocapitalization() -> TextInputAutocapitalization { guard selectedDataType == .barcode else { return .sentences } switch selectedBarcodeType { case .ean13, .ean8, .upce, .itf14: return .never case .code39, .code128, .pdf417: return .characters } } // MARK: - 计数/规则 private func getMaxLength() -> Int? { guard selectedDataType == .barcode else { return nil } switch selectedBarcodeType { case .ean13: return 13 case .ean8: return 8 case .upce: return 8 case .itf14: return 14 default: return nil } } private func requiresDigitsOnly() -> Bool { guard selectedDataType == .barcode else { return false } switch selectedBarcodeType { case .ean13, .ean8, .upce, .itf14: return true default: return false } } private func normalizedInputCount() -> Int { if requiresDigitsOnly() { return content.filter { $0.isNumber }.count } return content.count } // MARK: - 条形码格式提示 private func getBarcodeFormatHint() -> String { switch selectedBarcodeType { case .ean13: return "请输入13位数字,如:1234567890123" case .ean8: return "请输入8位数字,如:12345678" case .upce: return "请输入8位数字,如:12345678" case .code39: return "请输入字母、数字、空格和特殊字符" case .code128: return "请输入任意ASCII字符" case .itf14: return "请输入14位数字,如:12345678901234" case .pdf417: return "请输入任意ASCII字符" } } // MARK: - 输入框占位符文本 private func getPlaceholderText() -> String { if selectedDataType == .barcode { switch selectedBarcodeType { case .ean13: return "输入13位数字" case .ean8: return "输入8位数字" case .upce: return "输入8位数字" case .code39: return "输入字母、数字等" case .code128: return "输入任意字符" case .itf14: return "输入14位数字" case .pdf417: return "输入任意字符" } } else { return "请输入内容" } } // MARK: - 播放输入提示音 private func playInputSound() { // 使用系统触觉反馈作为输入提示音 let impactFeedback = UIImpactFeedbackGenerator(style: .light) impactFeedback.impactOccurred() } // MARK: - 判断输入是否完成 private func isInputComplete() -> Bool { if selectedDataType == .barcode { // 条形码需要验证格式正确 return validationResult?.isValid == true } else { // 二维码只要有内容就算完成 return !content.isEmpty } } private func createCode() { guard !content.isEmpty else { return } // 如果是条形码,验证格式 if selectedDataType == .barcode { let validation = BarcodeValidator.validateBarcode(content, type: selectedBarcodeType) if !validation.isValid { alertMessage = validation.errorMessage ?? "条形码格式不正确" showingAlert = true return } } let context = coreDataManager.container.viewContext let historyItem = HistoryItem(context: context) historyItem.id = UUID() historyItem.content = content historyItem.dataType = selectedDataType.rawValue historyItem.dataSource = DataSource.created.rawValue historyItem.createdAt = Date() historyItem.isFavorite = false if selectedDataType == .barcode { historyItem.barcodeType = selectedBarcodeType.rawValue } else { historyItem.qrCodeType = selectedQRCodeType.rawValue } coreDataManager.addHistoryItem(historyItem) alertMessage = "\(selectedDataType.displayName)创建成功!" showingAlert = true DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { dismiss() } } } #Preview { CreateCodeView() }