You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 lines
18 KiB

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()
}