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.

288 lines
10 KiB

import SwiftUI
// MARK: -
struct CodeContentInputView: View {
let selectedDataType: DataType
let selectedBarcodeType: BarcodeType
let selectedQRCodeType: QRCodeType
@Binding var content: String
@Binding var validationResult: BarcodeValidator.ValidationResult?
@FocusState.Binding var isContentFieldFocused: Bool
var body: some View {
VStack(spacing: 16) {
//
contentInputSection
//
if !content.isEmpty {
previewSection
}
}
}
// MARK: -
private var contentInputSection: some View {
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
handleContentChange(newValue)
}
//
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
)
}
}
}
}
// MARK: -
private var previewSection: some View {
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)
}
}
// MARK: -
private func handleContentChange(_ newValue: String) {
//
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)
}
private func validateBarcodeContent(_ newContent: String) {
guard selectedDataType == .barcode else { return }
validationResult = BarcodeValidator.validateBarcode(newContent, type: selectedBarcodeType)
}
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
}
}
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
}
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字符"
}
}
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 "请输入内容"
}
}
private func playInputSound() {
// 使
let impactFeedback = UIImpactFeedbackGenerator(style: .light)
impactFeedback.impactOccurred()
}
private func isInputComplete() -> Bool {
if selectedDataType == .barcode {
//
return validationResult?.isValid == true
} else {
//
return !content.isEmpty
}
}
}