fix: ControlSchema tolerate missing options field (Filler etc)
This commit is contained in:
parent
5ba2ae163b
commit
c72a018764
@ -80,43 +80,47 @@ struct MenuControl: View {
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func menuItemView(_ item: MenuItemSchema, depth: Int) -> some View {
|
||||
private func menuItemView(_ item: MenuItemSchema, depth: Int) -> AnyView {
|
||||
let hasChildren = !(item.submenu?.isEmpty ?? true)
|
||||
let isExpanded = expandedItems.contains(item.name)
|
||||
let isSelected = selectedItem == item.name
|
||||
|
||||
Button(action: { handleMenuClick(item, hasChildren: hasChildren) }) {
|
||||
HStack(spacing: 8) {
|
||||
if let icon = item.icon {
|
||||
Text(icon)
|
||||
.frame(width: 20)
|
||||
return AnyView(
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Button(action: { handleMenuClick(item, hasChildren: hasChildren) }) {
|
||||
HStack(spacing: 8) {
|
||||
if let icon = item.icon {
|
||||
Text(icon)
|
||||
.frame(width: 20)
|
||||
}
|
||||
Text(engine.i18n.t(item.label))
|
||||
.font(.subheadline)
|
||||
.lineLimit(1)
|
||||
Spacer()
|
||||
if hasChildren {
|
||||
Image(systemName: isExpanded ? "chevron.down" : "chevron.right")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 12 + CGFloat(depth * 16))
|
||||
.padding(.vertical, 8)
|
||||
.foregroundColor(isSelected ? .accentColor : .primary)
|
||||
.background(isSelected ? Color.accentColor.opacity(0.1) : Color.clear)
|
||||
.cornerRadius(6)
|
||||
}
|
||||
Text(engine.i18n.t(item.label))
|
||||
.font(.subheadline)
|
||||
.lineLimit(1)
|
||||
Spacer()
|
||||
if hasChildren {
|
||||
Image(systemName: isExpanded ? "chevron.down" : "chevron.right")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.buttonStyle(.plain)
|
||||
|
||||
// 子菜单
|
||||
if hasChildren && isExpanded {
|
||||
if let subitems = item.submenu {
|
||||
ForEach(subitems, id: \.name) { sub in
|
||||
menuItemView(sub, depth: depth + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 12 + CGFloat(depth * 16))
|
||||
.padding(.vertical, 8)
|
||||
.foregroundColor(isSelected ? .accentColor : .primary)
|
||||
.background(isSelected ? Color.accentColor.opacity(0.1) : Color.clear)
|
||||
.cornerRadius(6)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
// 子菜单
|
||||
if hasChildren && isExpanded {
|
||||
if let subitems = item.submenu {
|
||||
ForEach(subitems, id: \.name) { sub in
|
||||
menuItemView(sub, depth: depth + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private func handleMenuClick(_ item: MenuItemSchema, hasChildren: Bool) {
|
||||
@ -173,7 +177,11 @@ struct PopupWindowControl: View {
|
||||
.padding(12)
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
.background(Color(.systemBackground))
|
||||
#else
|
||||
.background(Color(nsColor: .windowBackgroundColor))
|
||||
#endif
|
||||
.cornerRadius(12)
|
||||
.shadow(radius: 8)
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ public final class BricksEngine: ObservableObject {
|
||||
}
|
||||
|
||||
/// 执行bind动作
|
||||
public func handleBind(_ bind: BindSchema, sourceId: String, eventData: EventData = [:]) async {
|
||||
public func handleBind(_ bind: BindSchema, sourceId: String, eventData: EventData = EventData()) async {
|
||||
guard let actionType = ActionType(rawValue: bind.actiontype) else {
|
||||
print("[SwiftBricks] Invalid actiontype: \(bind.actiontype)")
|
||||
return
|
||||
@ -306,7 +306,7 @@ public final class BricksEngine: ObservableObject {
|
||||
// MARK: - 触发事件
|
||||
|
||||
/// 触发widget事件
|
||||
public func triggerEvent(widgetId: String, event: String, data: EventData = [:]) async {
|
||||
public func triggerEvent(widgetId: String, event: String, data: EventData = EventData()) async {
|
||||
await eventBus.dispatch("\(widgetId).\(event)", data: data)
|
||||
}
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ public final class BricksEventBus: ObservableObject {
|
||||
// MARK: - 发布
|
||||
|
||||
/// 派发事件
|
||||
public func dispatch(_ eventName: String, data: EventData = [:]) async {
|
||||
public func dispatch(_ eventName: String, data: EventData = EventData()) async {
|
||||
// 记录日志
|
||||
eventLog.append((name: eventName, data: data, timestamp: Date()))
|
||||
if eventLog.count > maxLogSize {
|
||||
@ -50,7 +50,7 @@ public final class BricksEventBus: ObservableObject {
|
||||
}
|
||||
|
||||
/// 同步派发(用于UI事件)
|
||||
public func dispatchSync(_ eventName: String, data: EventData = [:]) {
|
||||
public func dispatchSync(_ eventName: String, data: EventData = EventData()) {
|
||||
Task { @MainActor in
|
||||
await dispatch(eventName, data: data)
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ public final class BricksRPC: ObservableObject {
|
||||
throw BricksRPCError.invalidResponse
|
||||
}
|
||||
|
||||
let responseHeaders = Dictionary(
|
||||
let responseHeaders: [String: String] = Dictionary(
|
||||
uniqueKeysWithValues: httpResponse.allHeaderFields.compactMap { key, value in
|
||||
guard let keyStr = key as? String, let valStr = value as? String else { return nil }
|
||||
return (keyStr, valStr)
|
||||
|
||||
@ -30,6 +30,20 @@ public struct ControlSchema: Codable, Identifiable, Sendable {
|
||||
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混合方式
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user