import SwiftUI /// 控件渲染器 — 递归将JSON schema渲染为SwiftUI视图 /// 这是SwiftBricks的核心,对应Bricks的widget实例化链 public struct ControlRenderer: View { let schema: ControlSchema @ObservedObject var engine: BricksEngine public init(schema: ControlSchema, engine: BricksEngine) { self.schema = schema self.engine = engine } public var body: some View { let pctHeight = parsePercentHeight(schema.options.height) renderWidget(schema) .frame( maxWidth: resolveWidth(schema.options), maxHeight: pctHeight != nil ? .infinity : resolveHeight(schema.options), alignment: resolveAlignment(schema.options) ) .layoutPriority(pctHeight ?? 0) } // MARK: - Widget分发 @ViewBuilder func renderWidget(_ schema: ControlSchema) -> some View { let type = schema.widgettype // 优先查找自定义注册的widget if let factory = engine.getWidgetFactory(type: type) { factory(schema, engine) } else { renderKnownWidget(schema, type: type) } } @ViewBuilder func renderKnownWidget(_ schema: ControlSchema, type: String) -> some View { switch type { // 文本类 case "Text": TextControl(schema: schema, engine: engine) case "Title1": TitleControl(schema: schema, engine: engine, level: 1) case "Title2": TitleControl(schema: schema, engine: engine, level: 2) case "Title3": TitleControl(schema: schema, engine: engine, level: 3) case "Title4": TitleControl(schema: schema, engine: engine, level: 4) case "Title5": TitleControl(schema: schema, engine: engine, level: 5) case "Title6": TitleControl(schema: schema, engine: engine, level: 6) case "Label": LabelControl(schema: schema, engine: engine) // 输入类 case "Input", "UiStr": InputControl(schema: schema, engine: engine) case "Textarea", "UiText": TextareaControl(schema: schema, engine: engine) case "UiNumber": NumberControl(schema: schema, engine: engine) case "UiDate": DateControl(schema: schema, engine: engine) case "Select", "UiCode": SelectControl(schema: schema, engine: engine) // 按钮 case "Button": ButtonControl(schema: schema, engine: engine) // 布局 case "VBox", "Card": VBoxControl(schema: schema, engine: engine) case "HBox": HBoxControl(schema: schema, engine: engine) case "VScrollPanel", "HScrollPanel": ScrollPanelControl(schema: schema, engine: engine) case "Filler": Spacer().frame(maxWidth: .infinity, maxHeight: .infinity) case "DynamicColumn": DynamicColumnControl(schema: schema, engine: engine) // 数据展示 case "Tabular", "DataViewer": TabularControl(schema: schema, engine: engine) case "Form": FormControl(schema: schema, engine: engine) case "InlineForm": InlineFormControl(schema: schema, engine: engine) // 导航 case "TabView": TabViewControl(schema: schema, engine: engine) case "Menu": MenuControl(schema: schema, engine: engine) // 弹窗 case "PopupWindow": PopupWindowControl(schema: schema, engine: engine) // HTML case "Html": HtmlControl(schema: schema, engine: engine) // Image case "Image": ImageControl(schema: schema, engine: engine) // VideoPlayer case "VideoPlayer": VideoPlayerControl(schema: schema, engine: engine) // urlwidget(直接加载远程内容) case "urlwidget": UrlWidgetControl(schema: schema, engine: engine) default: // 未知widgettype — 渲染占位符 VStack { Text("⚠️ Unknown: \(type)") .font(.caption) .foregroundColor(.secondary) } } } // MARK: - 尺寸解析 private func resolveWidth(_ opts: ControlOptions) -> CGFloat? { if let w = opts.width { if w == "100%" { return nil } // 使用frame的maxWidth if let px = parsePixels(w) { return px } } return nil } private func resolveHeight(_ opts: ControlOptions) -> CGFloat? { if let h = opts.height { if h == "100%" { return nil } if let px = parsePixels(h) { return px } } return nil } private func resolveAlignment(_ opts: ControlOptions) -> Alignment { switch opts.alignItems { case "center": return .center case "flex-end", "end": return .trailing case "flex-start", "start": return .leading default: return .topLeading } } private func parsePercentHeight(_ h: String?) -> Double? { guard let h = h, h.hasSuffix("%"), let val = Double(h.dropLast()) else { return nil } return val > 0 ? val : nil } private func parsePixels(_ str: String) -> CGFloat? { let cleaned = str.replacingOccurrences(of: "px", with: "") return CGFloat(Double(cleaned) ?? 0) } } // MARK: - 渲染子控件列表 struct SubWidgetsView: View { let subwidgets: [ControlSchema] @ObservedObject var engine: BricksEngine var body: some View { ForEach(Array(subwidgets.enumerated()), id: \.offset) { _, sub in ControlRenderer(schema: sub, engine: engine) } } }