import SwiftUI // MARK: - Input Control struct InputControl: View { let schema: ControlSchema @ObservedObject var engine: BricksEngine @State private var text: String = "" @State private var isFocused: Bool = false @State private var errorMessage: String = "" var body: some View { VStack(alignment: .leading, spacing: 4) { if showLabel { Text(effectiveLabel) .font(.caption) .foregroundColor(.secondary) } TextField(placeholder, text: $text) .textFieldStyle(.roundedBorder) .onChange(of: text) { _, newValue in let formId = findParentFormId() if let formId, let name = schema.options.name { engine.store.setFormField(formId: formId, field: name, value: newValue) } engine.store.setValue(id: schema.effectiveId, value: newValue) if isFocused { clearError() } } .overlay( RoundedRectangle(cornerRadius: 6) .stroke(errorMessage.isEmpty ? Color.clear : Color.red, lineWidth: 1) ) if !errorMessage.isEmpty { Text(errorMessage) .font(.caption2) .foregroundColor(.red) } } .onAppear { text = schema.options.value ?? schema.options.defaultvalue ?? "" let formId = findParentFormId() if let formId, let name = schema.options.name { text = engine.store.getFormField(formId: formId, field: name) if text.isEmpty { text = schema.options.value ?? schema.options.defaultvalue ?? "" } } } } private var effectiveLabel: String { schema.options.label ?? schema.options.title ?? schema.options.name ?? "" } private var placeholder: String { let raw = schema.options.placeholder ?? "" return raw.isEmpty ? effectiveLabel : engine.i18n.t(raw) } private var showLabel: Bool { true } private func findParentFormId() -> String? { // 简化实现:使用schema id作为form标识 schema.options.name } private func clearError() { errorMessage = "" } } // MARK: - Textarea Control struct TextareaControl: View { let schema: ControlSchema @ObservedObject var engine: BricksEngine @State private var text: String = "" var body: some View { VStack(alignment: .leading, spacing: 4) { if let label = schema.options.label ?? schema.options.title { Text(engine.i18n.t(label)) .font(.caption) .foregroundColor(.secondary) } #if os(iOS) TextEditor(text: $text) .frame(minHeight: 100) .overlay( RoundedRectangle(cornerRadius: 6) .stroke(Color.secondary.opacity(0.3), lineWidth: 1) ) #else TextEditor(text: $text) .frame(minHeight: 100) .border(Color.secondary.opacity(0.3)) #endif } .onAppear { text = schema.options.value ?? "" } .onChange(of: text) { _, newValue in engine.store.setValue(id: schema.effectiveId, value: newValue) } } } // MARK: - Number Control struct NumberControl: View { let schema: ControlSchema @ObservedObject var engine: BricksEngine @State private var value: Double = 0 var body: some View { VStack(alignment: .leading, spacing: 4) { if let label = schema.options.label ?? schema.options.title { Text(engine.i18n.t(label)) .font(.caption) .foregroundColor(.secondary) } HStack { Button("−") { value -= step } .buttonStyle(.bordered) Text(String(format: format, value)) .frame(minWidth: 60) .multilineTextAlignment(.center) Button("+") { value += step } .buttonStyle(.bordered) } } .onAppear { value = Double(schema.options.value ?? "0") ?? 0 } .onChange(of: value) { _, newValue in engine.store.setValue(id: schema.effectiveId, value: "\(newValue)") } } private var step: Double { 1 } private var format: String { "%g" } } // MARK: - Date Control struct DateControl: View { let schema: ControlSchema @ObservedObject var engine: BricksEngine @State private var date: Date = Date() var body: some View { VStack(alignment: .leading, spacing: 4) { if let label = schema.options.label ?? schema.options.title { Text(engine.i18n.t(label)) .font(.caption) .foregroundColor(.secondary) } DatePicker("", selection: $date, displayedComponents: [.date]) .labelsHidden() } .onChange(of: date) { _, newValue in let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" engine.store.setValue(id: schema.effectiveId, value: formatter.string(from: newValue)) } } }