342 lines
9.6 KiB
Swift
342 lines
9.6 KiB
Swift
import Foundation
|
||
|
||
// MARK: - JSON Schema Models (Bricks JSON → Codable)
|
||
|
||
/// 根控件定义,对应Bricks JSON顶层结构
|
||
public struct ControlSchema: Codable, Identifiable, Sendable {
|
||
public var id: String?
|
||
public let widgettype: String
|
||
public var options: ControlOptions
|
||
public var binds: [BindSchema]?
|
||
public var subwidgets: [ControlSchema]?
|
||
|
||
/// 运行时生成的唯一ID(当JSON未提供id时)
|
||
public var runtimeId: String = UUID().uuidString.prefix(8).lowercased()
|
||
|
||
public var effectiveId: String {
|
||
id ?? runtimeId
|
||
}
|
||
|
||
public init(
|
||
id: String? = nil,
|
||
widgettype: String,
|
||
options: ControlOptions = .init(),
|
||
binds: [BindSchema]? = nil,
|
||
subwidgets: [ControlSchema]? = nil
|
||
) {
|
||
self.id = id
|
||
self.widgettype = widgettype
|
||
self.options = options
|
||
self.binds = binds
|
||
self.subwidgets = subwidgets
|
||
}
|
||
|
||
/// 自定义解码:options缺失时默认为空,避免Filler等无options的widget解码失败
|
||
public init(from decoder: Decoder) throws {
|
||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||
self.id = try container.decodeIfPresent(String.self, forKey: .id)
|
||
self.widgettype = try container.decode(String.self, forKey: .widgettype)
|
||
self.options = try container.decodeIfPresent(ControlOptions.self, forKey: .options) ?? ControlOptions()
|
||
self.binds = try container.decodeIfPresent([BindSchema].self, forKey: .binds)
|
||
self.subwidgets = try container.decodeIfPresent([ControlSchema].self, forKey: .subwidgets)
|
||
}
|
||
|
||
private enum CodingKeys: String, CodingKey {
|
||
case id, widgettype, options, binds, subwidgets
|
||
}
|
||
}
|
||
|
||
/// 控件选项 — 使用字典+Codable混合方式
|
||
public struct ControlOptions: Codable, Sendable {
|
||
// 通用属性
|
||
public var width: String?
|
||
public var height: String?
|
||
public var cwidth: Double?
|
||
public var cheight: Double?
|
||
public var bgcolor: String?
|
||
public var color: String?
|
||
public var css: String?
|
||
public var padding: String?
|
||
public var cursor: String?
|
||
public var spacing: Double?
|
||
public var alignItems: String?
|
||
|
||
// Text/Title
|
||
public var text: String?
|
||
public var otext: String?
|
||
public var i18n: Bool?
|
||
public var level: Int?
|
||
|
||
// Button
|
||
public var label: String?
|
||
public var actiontype: String?
|
||
public var url: String?
|
||
public var icon: String?
|
||
|
||
// Input/Textarea
|
||
public var name: String?
|
||
public var placeholder: String?
|
||
public var value: String?
|
||
public var defaultvalue: String?
|
||
public var required: Bool?
|
||
public var uitype: String?
|
||
public var title: String?
|
||
public var rules: [ValidationRule]?
|
||
public var codes: [CodeItem]?
|
||
|
||
// Select/Code
|
||
public var data: [CodeItem]?
|
||
public var dataurl: String?
|
||
public var valueField: String?
|
||
public var textField: String?
|
||
|
||
// Form
|
||
public var fields: [FieldSchema]?
|
||
public var submit_url: String?
|
||
public var submit_label: String?
|
||
public var show_label: Bool?
|
||
|
||
// Tabular
|
||
public var data_url: String?
|
||
public var data_method: String?
|
||
public var page_rows: Int?
|
||
public var row_options: RowOptions?
|
||
public var toolbar: ToolbarSchema?
|
||
|
||
// TabView
|
||
public var tabs: [TabItem]?
|
||
public var activeTab: Int?
|
||
|
||
// DynamicColumn
|
||
public var col_cwidth: Double?
|
||
public var col_cgap: Double?
|
||
public var col_width: Double?
|
||
|
||
// PopupWindow
|
||
public var popup_options: PopupOptions?
|
||
public var auto_open: Bool?
|
||
public var archor: String?
|
||
public var dismiss_events: [String]?
|
||
|
||
// Menu
|
||
public var items: [MenuItemSchema]?
|
||
public var target: String?
|
||
|
||
// HTML
|
||
public var html: String?
|
||
|
||
// Image
|
||
public var src: String?
|
||
public var alt: String?
|
||
|
||
// Misc
|
||
public var fontsize: String?
|
||
public var cfontsize: Double?
|
||
|
||
public init() {}
|
||
}
|
||
|
||
/// 绑定定义 — event→action映射
|
||
public struct BindSchema: Codable, Sendable {
|
||
public let wid: String // 事件源widget id
|
||
public let event: String // 事件名
|
||
public let actiontype: String // 动作类型(9种)
|
||
public var target: String? // 目标widget id
|
||
public var options: BindOptions?
|
||
public var mode: String? // replace/append
|
||
public var method: String? // method名(actiontype=method时)
|
||
public var script: String? // 内联脚本(actiontype=script时)
|
||
public var popup_options: PopupOptions?
|
||
public var datawidget: String? // 数据源widget
|
||
public var event_params: String?
|
||
}
|
||
|
||
/// 绑定选项
|
||
public struct BindOptions: Codable, Sendable {
|
||
public var url: String?
|
||
public var params: [String: String]?
|
||
|
||
public init(url: String? = nil, params: [String: String]? = nil) {
|
||
self.url = url
|
||
self.params = params
|
||
}
|
||
}
|
||
|
||
/// 弹出窗口选项
|
||
public struct PopupOptions: Codable, Sendable {
|
||
public var title: String?
|
||
public var cwidth: Double?
|
||
public var cheight: Double?
|
||
public var width: String?
|
||
public var height: String?
|
||
public var archor: String?
|
||
public var eventpos: Bool?
|
||
public var dismiss_events: [String]?
|
||
|
||
public init() {}
|
||
}
|
||
|
||
/// 表单字段定义
|
||
public struct FieldSchema: Codable, Sendable {
|
||
public var name: String
|
||
public var label: String?
|
||
public var title: String?
|
||
public var type: String?
|
||
public var uitype: String?
|
||
public var required: Bool?
|
||
public var cwidth: Double?
|
||
public var placeholder: String?
|
||
public var value: String?
|
||
public var defaultvalue: String?
|
||
public var codes: [CodeItem]?
|
||
public var data: [CodeItem]?
|
||
public var dataurl: String?
|
||
public var valueField: String?
|
||
public var textField: String?
|
||
public var rules: [ValidationRule]?
|
||
|
||
public var effectiveLabel: String { label ?? title ?? name }
|
||
public var effectiveUitype: String { uitype ?? type ?? "str" }
|
||
}
|
||
|
||
/// 验证规则
|
||
public struct ValidationRule: Codable, Sendable {
|
||
public let type: String
|
||
public var value: String?
|
||
public var message: String?
|
||
}
|
||
|
||
/// 代码项(下拉选项)
|
||
public struct CodeItem: Codable, Sendable, Identifiable {
|
||
public var id: String { value }
|
||
public let value: String
|
||
public let text: String
|
||
}
|
||
|
||
/// Tabular行选项
|
||
public struct RowOptions: Codable, Sendable {
|
||
public var fields: [FieldSchema]?
|
||
public var browserfields: BrowserFields?
|
||
}
|
||
|
||
/// 浏览器字段配置
|
||
public struct BrowserFields: Codable, Sendable {
|
||
public var exclouded: [String]?
|
||
public var cwidths: [String: Double]?
|
||
public var alters: [String: FieldSchema]?
|
||
}
|
||
|
||
/// 工具栏定义
|
||
public struct ToolbarSchema: Codable, Sendable {
|
||
public var tools: [ToolbarTool]?
|
||
}
|
||
|
||
/// 工具栏按钮
|
||
public struct ToolbarTool: Codable, Sendable {
|
||
public var name: String
|
||
public var label: String?
|
||
public var icon: String?
|
||
public var selected_row: Bool?
|
||
public var css: String?
|
||
}
|
||
|
||
/// TabView项
|
||
public struct TabItem: Codable, Sendable {
|
||
public var label: String
|
||
public var icon: String?
|
||
public var content: ControlSchema?
|
||
}
|
||
|
||
/// 菜单项
|
||
public struct MenuItemSchema: Codable, Sendable {
|
||
public var name: String
|
||
public var label: String
|
||
public var icon: String?
|
||
public var url: String?
|
||
public var submenu: [MenuItemSchema]?
|
||
public var binds: [BindSchema]?
|
||
}
|
||
|
||
// MARK: - 有效WidgetType枚举
|
||
|
||
public enum WidgetType: String, CaseIterable, Sendable {
|
||
// 文本
|
||
case text = "Text"
|
||
case title1 = "Title1", title2 = "Title2", title3 = "Title3"
|
||
case title4 = "Title4", title5 = "Title5", title6 = "Title6"
|
||
case label = "Label"
|
||
|
||
// 输入
|
||
case input = "Input"
|
||
case textarea = "Textarea"
|
||
case select = "Select"
|
||
case uiCode = "UiCode"
|
||
case uiStr = "UiStr"
|
||
case uiNumber = "UiNumber"
|
||
case uiDate = "UiDate"
|
||
case uiText = "UiText"
|
||
|
||
// 按钮
|
||
case button = "Button"
|
||
|
||
// 布局
|
||
case vbox = "VBox"
|
||
case hbox = "HBox"
|
||
case vScrollPanel = "VScrollPanel"
|
||
case hScrollPanel = "HScrollPanel"
|
||
case filler = "Filler"
|
||
case dynamicColumn = "DynamicColumn"
|
||
|
||
// 数据
|
||
case tabular = "Tabular"
|
||
case dataViewer = "DataViewer"
|
||
case form = "Form"
|
||
case inlineForm = "InlineForm"
|
||
|
||
// 导航
|
||
case tabView = "TabView"
|
||
case menu = "Menu"
|
||
|
||
// 弹窗
|
||
case popupWindow = "PopupWindow"
|
||
|
||
// 其他
|
||
case card = "Card"
|
||
case html = "Html"
|
||
case image = "Image"
|
||
case urlwidget = "urlwidget"
|
||
|
||
// 特殊
|
||
case popup = "Popup"
|
||
|
||
public var isLayout: Bool {
|
||
[.vbox, .hbox, .vScrollPanel, .hScrollPanel, .dynamicColumn, .card].contains(self)
|
||
}
|
||
|
||
public var isText: Bool {
|
||
[.text, .title1, .title2, .title3, .title4, .title5, .title6, .label].contains(self)
|
||
}
|
||
|
||
public var isInput: Bool {
|
||
[.input, .textarea, .select, .uiCode, .uiStr, .uiNumber, .uiDate, .uiText].contains(self)
|
||
}
|
||
}
|
||
|
||
// MARK: - 有效ActionType枚举
|
||
|
||
public enum ActionType: String, CaseIterable, Sendable {
|
||
case newwindow // 新窗口打开
|
||
case iframe // iframe加载
|
||
case urlwidget // 加载UI内容到目标(最常用)
|
||
case urldata // 获取数据更新目标
|
||
case bricks // 从配置实例化widget
|
||
case registerfunction // 注册函数
|
||
case method // 调用目标widget方法
|
||
case script // 执行内联脚本
|
||
case event // 派发自定义事件
|
||
|
||
public static var validNames: [String] {
|
||
allCases.map { $0.rawValue }
|
||
}
|
||
}
|