import SwiftUI struct InstanceFormView: View { @Environment(\.dismiss) private var dismiss @ObservedObject var model: FleetViewModel @State private var name = "" @State private var originText = "" @State private var tagsText = "" @State private var password = "" @State private var validationMessage: String? @State private var isSaving = false private var canSave: Bool { !name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !originText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } var body: some View { NavigationStack { ScrollView { VStack(spacing: 12) { AICard { VStack(alignment: .leading, spacing: 14) { field("名称", text: $name, placeholder: "Home Node") field("地址", text: $originText, placeholder: "http://192.168.1.10:3000") field("标签", text: $tagsText, placeholder: "home, primary") field("实例密码", text: $password, placeholder: "可选,保存在本机沙盒", secure: true) } } if let validationMessage { AIStatusBanner( validationMessage, tint: AITheme.danger, systemImage: "exclamationmark.triangle") } } .padding(16) .padding(.bottom, 8) } .aiBottomActionBar { AIActionRow { Button("取消") { dismiss() } .buttonStyle(AIButtonStyle(kind: .secondary, size: .regular, fillsWidth: true)) Button { save() } label: { HStack(spacing: 8) { if isSaving { ProgressView() .controlSize(.small) } Text(isSaving ? "保存中" : "保存实例") } } .buttonStyle(AIButtonStyle(kind: .primary, size: .regular, fillsWidth: true)) .disabled(!canSave || isSaving) } } .background(AITheme.canvas.ignoresSafeArea()) .navigationTitle("添加实例") #if os(iOS) .scrollDismissesKeyboard(.interactively) #endif } } private func field( _ title: String, text: Binding, placeholder: String, secure: Bool = false ) -> some View { VStack(alignment: .leading, spacing: 6) { AIFieldLabel(title) if secure { SecureField(placeholder, text: text) .textFieldStyle(AIFieldStyle()) .textContentType(.password) .accessibilityLabel(title) } else { TextField(placeholder, text: text) .textFieldStyle(AIFieldStyle()) .accessibilityLabel(title) .submitLabel(title == "地址" ? .next : .done) #if os(iOS) .keyboardType(title == "地址" ? .URL : .default) .textInputAutocapitalization(title == "地址" || title == "标签" ? .never : nil) #endif .autocorrectionDisabled(title == "地址" || title == "标签") } } } private func save() { guard !isSaving else { return } isSaving = true let normalizedName = name.trimmingCharacters(in: .whitespacesAndNewlines) guard let origin = URL(string: originText.trimmingCharacters(in: .whitespacesAndNewlines)), origin.scheme == "http" || origin.scheme == "https", origin.host != nil else { validationMessage = "请输入有效的 HTTP 或 HTTPS 根地址。" isSaving = false return } let tags = tagsText .split(separator: ",") .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty } let input = InstanceInput( name: normalizedName, origin: origin, tags: tags, password: password.isEmpty ? nil : password ) Task { do { try await model.add(input) } catch { validationMessage = "保存失败,请检查地址是否重复。" } isSaving = false } } }