feat:图表交互
This commit is contained in:
@@ -22,12 +22,13 @@ def get_catalog_cache() -> ComponentCatalog | None:
|
||||
# 手写的物料定义目录,只供导入脚本读取后写入 MongoDB,运行时不读取
|
||||
MATERIALS_DIR = Path(__file__).resolve().parent / "materials"
|
||||
FIELD_GROUPS_FILE = "field_groups.json"
|
||||
EVENT_GROUPS_FILE = "event_groups.json"
|
||||
|
||||
|
||||
def load_material_definitions(
|
||||
directory: str | Path = MATERIALS_DIR,
|
||||
) -> ComponentCatalog:
|
||||
"""读取物料定义目录:共享字段分组一个文件,每个物料一个文件;不合法直接报错
|
||||
"""读取物料定义目录:共享字段分组、共享事件组各一个文件,每个物料一个文件;不合法直接报错
|
||||
|
||||
version 为全部文件内容的摘要,定义有任何改动都会得到新版本
|
||||
"""
|
||||
@@ -38,13 +39,15 @@ def load_material_definitions(
|
||||
if not any(path.name == FIELD_GROUPS_FILE for path in files):
|
||||
raise FileNotFoundError(f"物料定义目录缺少 {FIELD_GROUPS_FILE}: {target}")
|
||||
digest = hashlib.sha256()
|
||||
components, field_groups = [], []
|
||||
components, field_groups, event_groups = [], [], []
|
||||
for path in files:
|
||||
# utf-8-sig 同时兼容带 BOM 和不带 BOM 的 UTF-8 文件
|
||||
text = path.read_text(encoding="utf-8-sig")
|
||||
digest.update(path.name.encode() + b"\0" + text.encode())
|
||||
if path.name == FIELD_GROUPS_FILE:
|
||||
field_groups = json.loads(text)
|
||||
elif path.name == EVENT_GROUPS_FILE:
|
||||
event_groups = json.loads(text)
|
||||
else:
|
||||
components.append(json.loads(text))
|
||||
# 文件名排序与展示顺序无关,物料按定义中的 order 排列,未写时排在最后
|
||||
@@ -57,5 +60,6 @@ def load_material_definitions(
|
||||
for item in components
|
||||
],
|
||||
"field_groups": field_groups,
|
||||
"event_groups": event_groups,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import re
|
||||
|
||||
from pydantic import JsonValue
|
||||
|
||||
from app.schemas.actions.set_interaction import INTERACTION_KEYS
|
||||
from app.schemas.base import JsonObject
|
||||
from app.schemas.component_resource import (
|
||||
ComponentCatalog,
|
||||
@@ -17,6 +18,7 @@ from app.schemas.material_field import (
|
||||
MaterialField,
|
||||
lookup,
|
||||
)
|
||||
from app.schemas.screen import ScreenNode
|
||||
|
||||
|
||||
def resolve_field_groups(
|
||||
@@ -69,6 +71,8 @@ def frontend_catalog(catalog: ComponentCatalog) -> dict:
|
||||
for item, component in zip(data["components"], catalog.components, strict=True):
|
||||
item["setters"] = panel_setters(resolve_field_groups(catalog, component))
|
||||
item["defaults"] = structural_defaults(catalog, component)
|
||||
# 定义里引用共享事件组,前端仍拿展开后的数组
|
||||
item["eventOptions"] = catalog.event_options(component)
|
||||
return data
|
||||
|
||||
|
||||
@@ -108,9 +112,13 @@ def find_material(
|
||||
return matches[0]
|
||||
|
||||
|
||||
def material_payload(component: ComponentDefinition) -> dict:
|
||||
"""给模型看的物料配置:完整默认 schema 及使用说明,字段定义由字段查询按需提供"""
|
||||
return component.model_dump(mode="json", by_alias=True, exclude={"field_groups"})
|
||||
def material_payload(catalog: ComponentCatalog, component: ComponentDefinition) -> dict:
|
||||
"""给模型看的物料配置:完整默认 schema、可用事件及使用说明,字段定义由字段查询按需提供"""
|
||||
data = component.model_dump(
|
||||
mode="json", by_alias=True, exclude={"field_groups", "event_groups"}
|
||||
)
|
||||
data["eventOptions"] = catalog.event_options(component)
|
||||
return data
|
||||
|
||||
|
||||
def list_components(catalog: ComponentCatalog) -> dict:
|
||||
@@ -142,7 +150,9 @@ def get_materials(catalog: ComponentCatalog, component_types: list[str]) -> dict
|
||||
return {
|
||||
"catalog_version": catalog.version,
|
||||
"materials": [
|
||||
material_payload(item) for item in catalog.components if item.type in types
|
||||
material_payload(catalog, item)
|
||||
for item in catalog.components
|
||||
if item.type in types
|
||||
],
|
||||
}
|
||||
|
||||
@@ -161,6 +171,7 @@ def describe_field(
|
||||
"themed",
|
||||
"example",
|
||||
"identifies",
|
||||
"template",
|
||||
},
|
||||
exclude_none=True,
|
||||
exclude_defaults=True,
|
||||
@@ -278,6 +289,8 @@ def check_update_field(
|
||||
}
|
||||
path = action.field_path
|
||||
field = fields.get(path)
|
||||
if field is None and (top := _node_top_field(action)):
|
||||
raise ValueError(top)
|
||||
if field is None:
|
||||
message = f"{node_type} 没有字段 {path},只能修改字段定义中的路径"
|
||||
keyword = next(
|
||||
@@ -295,6 +308,31 @@ def check_update_field(
|
||||
raise ValueError(f"字段 {path}({field.label}){error}")
|
||||
|
||||
|
||||
# 组件顶层字段不能用 update 改时该用什么
|
||||
_TOP_FIELD_HINTS = {
|
||||
"layout": "位置和尺寸用 move、resize 或 set_layout",
|
||||
"name": "组件名称用 update 的 section=name、path=[]",
|
||||
}
|
||||
|
||||
|
||||
def _node_top_field(action) -> str | None:
|
||||
"""props / style 下的路径第一段是组件的顶层字段(visibleWhen、filters、layout……)时的说明,否则为 None
|
||||
|
||||
模型把 filters 写成 section=props、path=["filters"],只说"没有字段 props.filters"时它不知道该换哪个动作
|
||||
"""
|
||||
if action.section == "name" or not action.path:
|
||||
return None
|
||||
key = action.path[0]
|
||||
# props.style.color 这类多套了一层:props、style 本身能用 update 改,交给后面按字段名给建议
|
||||
if key not in ScreenNode.model_fields or key in ("props", "style"):
|
||||
return None
|
||||
if key in INTERACTION_KEYS:
|
||||
hint = "显示条件、数据筛选和事件用 set_interaction 设置"
|
||||
else:
|
||||
hint = _TOP_FIELD_HINTS.get(key, "不能用 update 修改")
|
||||
return f"{key} 是组件的顶层字段,不在 {action.section} 下;{hint}"
|
||||
|
||||
|
||||
def component_fields(
|
||||
catalog: ComponentCatalog, component: ComponentDefinition
|
||||
) -> list[MaterialField]:
|
||||
@@ -334,6 +372,7 @@ def retained_examples(
|
||||
"""组件上还和默认配置一样的示例内容(标题、系列名、数据……),按字段定义的 example 判断
|
||||
|
||||
只比较现在的值,不管是新增时没写、写了一样的值,还是之后又改回来;物料不存在时为空
|
||||
有数据行时被 props.bind 覆盖的字段不算:画面上显示的是数据行里的值,留着的示例值不会出现
|
||||
"""
|
||||
if catalog is None:
|
||||
return []
|
||||
@@ -342,9 +381,10 @@ def retained_examples(
|
||||
except ValueError:
|
||||
return []
|
||||
template = component.template()
|
||||
bound = bound_paths(component, node)
|
||||
retained = []
|
||||
for field in component_fields(catalog, component):
|
||||
if not field.example:
|
||||
if not field.example or field.path in bound:
|
||||
continue
|
||||
try:
|
||||
current = lookup(node, field.segments)
|
||||
@@ -356,6 +396,19 @@ def retained_examples(
|
||||
return retained
|
||||
|
||||
|
||||
def bound_paths(component: ComponentDefinition, node: dict) -> set[str]:
|
||||
"""props.bind 覆盖的字段路径
|
||||
|
||||
不看数据行是否已经写了:同一批里常先加卡片、再填 rows,中途提示"沿用了示例数据"会让模型去手写静态值;
|
||||
交付时 bind 配空数据行由 interaction_problems 拦下
|
||||
"""
|
||||
props = node.get("props") or {}
|
||||
bind = props.get("bind")
|
||||
if component.rows_path is None or not isinstance(bind, dict) or not bind:
|
||||
return set()
|
||||
return {f"props.{key}" for key in bind}
|
||||
|
||||
|
||||
# 摘要里每段文字的长度上限:文本组件的内容可能很长,认出组件只需要开头
|
||||
IDENTITY_MAX_CHARS = 40
|
||||
|
||||
@@ -378,12 +431,26 @@ def identifying_texts(catalog: ComponentCatalog | None, node: dict) -> list[str]
|
||||
text
|
||||
if len(text) <= IDENTITY_MAX_CHARS
|
||||
else text[:IDENTITY_MAX_CHARS] + "…"
|
||||
for value in _values_at(node, field.segments)
|
||||
if isinstance(value, str) and (text := value.strip())
|
||||
for value in _identity_values(node, field)
|
||||
if (text := value.strip())
|
||||
)
|
||||
return texts
|
||||
|
||||
|
||||
def _identity_values(node: dict, field: MaterialField) -> list[str]:
|
||||
"""识别字段的文字:字符串字段取它本身,选项数组取每项的 label"""
|
||||
values = values_at(node, field.segments)
|
||||
if field.value_type == "array":
|
||||
values = [
|
||||
item.get("label")
|
||||
for value in values
|
||||
if isinstance(value, list)
|
||||
for item in value
|
||||
if isinstance(item, dict)
|
||||
]
|
||||
return [value for value in values if isinstance(value, str)]
|
||||
|
||||
|
||||
# 识别字段(标题、名称、文本内容)在画面上是一行短文字。换行、超过这个长度或带句号分号时是一段话:
|
||||
# 线上问答之后"把结果放到大屏上",模型把"关键发现 1. …"、"注:……;……。"写进了文本组件
|
||||
LABEL_MAX_CHARS = 60
|
||||
@@ -413,19 +480,20 @@ def paragraph_texts(
|
||||
(field, value)
|
||||
for field in component_fields(catalog, component)
|
||||
if field.identifies and (paths is None or field.path in paths)
|
||||
for value in _values_at(node, field.segments)
|
||||
if isinstance(value, str) and not is_label(value)
|
||||
for value in _identity_values(node, field)
|
||||
if not is_label(value)
|
||||
]
|
||||
|
||||
|
||||
def _values_at(value: JsonValue, segments: list[str | int]) -> list[JsonValue]:
|
||||
def values_at(value: JsonValue, segments: list[str | int]) -> list[JsonValue]:
|
||||
"""路径上的所有值:路径途经数组而字段按对象写时(ECharts 的多个 title),每一项都取"""
|
||||
if not segments:
|
||||
return [value]
|
||||
head, *rest = segments
|
||||
if isinstance(value, dict) and isinstance(head, str) and head in value:
|
||||
return _values_at(value[head], rest)
|
||||
return values_at(value[head], rest)
|
||||
if isinstance(value, list) and isinstance(head, int) and 0 <= head < len(value):
|
||||
return _values_at(value[head], rest)
|
||||
return values_at(value[head], rest)
|
||||
if isinstance(value, list) and isinstance(head, str):
|
||||
return [found for item in value for found in _values_at(item, segments)]
|
||||
return [found for item in value for found in values_at(item, segments)]
|
||||
return []
|
||||
|
||||
@@ -44,44 +44,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-xy"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "area-chart",
|
||||
"name": "面积图",
|
||||
|
||||
@@ -121,44 +121,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-xy"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "bar-chart",
|
||||
"name": "柱状图",
|
||||
|
||||
@@ -205,44 +205,12 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "bar-line-chart",
|
||||
"name": "柱线混合图",
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
[
|
||||
{
|
||||
"key": "dom-events",
|
||||
"options": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "chart-events",
|
||||
"options": [
|
||||
{
|
||||
"label": "点击数据项",
|
||||
"value": "itemClick"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -71,44 +71,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-item"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "funnel-chart",
|
||||
"name": "漏斗图",
|
||||
|
||||
@@ -100,44 +100,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-item"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "gauge-chart",
|
||||
"name": "仪表盘",
|
||||
|
||||
@@ -205,44 +205,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-xy"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "hbar-chart",
|
||||
"name": "条形图",
|
||||
|
||||
@@ -198,44 +198,12 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "heatmap-chart",
|
||||
"name": "热力图",
|
||||
|
||||
@@ -26,44 +26,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-xy"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "line-chart",
|
||||
"name": "折线图",
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
{
|
||||
"order": 18,
|
||||
"key": "nav",
|
||||
"type": "nav",
|
||||
"name": "选项导航",
|
||||
"description": "一组可点击的选项(页签、模型选择器),点击某项把它的 value 写进变量,值等于当前变量的项高亮;配合组件的显示条件做整页切换,配合筛选做钻取",
|
||||
"tags": [
|
||||
"info",
|
||||
"导航",
|
||||
"页签",
|
||||
"切换",
|
||||
"选择器",
|
||||
"tabs",
|
||||
"nav"
|
||||
],
|
||||
"renderer": "nav",
|
||||
"group": "info",
|
||||
"icon": "solar:list-bold",
|
||||
"usage_notes": [
|
||||
"props.var 写变量 ID,变量先在画布 variables 里声明;点击选项写变量不需要配事件",
|
||||
"items 每项 {label, value},value 不能为空且不重复;可以写 $query,例如 SELECT DISTINCT model AS label, model AS value FROM t ORDER BY 1",
|
||||
"变量的默认值取 items 里的某个 value,打开大屏时那一项高亮"
|
||||
],
|
||||
"field_groups": [
|
||||
"node-base",
|
||||
{
|
||||
"key": "nav-content",
|
||||
"label": "选项",
|
||||
"description": "写哪个变量、有哪些选项、横排还是竖排",
|
||||
"fields": [
|
||||
{
|
||||
"path": "props.var",
|
||||
"label": "变量",
|
||||
"description": "点击选项时写入的变量 ID,值等于该变量的选项高亮",
|
||||
"value_type": "string",
|
||||
"aliases": [
|
||||
"绑定变量",
|
||||
"变量名"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "props.items",
|
||||
"label": "选项",
|
||||
"description": "选项列表,每项 {label: 显示文字, value: 写入变量的值}",
|
||||
"value_type": "array",
|
||||
"item_type": "object",
|
||||
"panel": false,
|
||||
"example": "label",
|
||||
"identifies": true,
|
||||
"aliases": [
|
||||
"选项列表",
|
||||
"页签",
|
||||
"标签页",
|
||||
"菜单项"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "props.direction",
|
||||
"label": "排列方向",
|
||||
"description": "选项横排还是竖排",
|
||||
"value_type": "enum",
|
||||
"options": [
|
||||
{
|
||||
"value": "horizontal",
|
||||
"label": "横排"
|
||||
},
|
||||
{
|
||||
"value": "vertical",
|
||||
"label": "竖排"
|
||||
}
|
||||
],
|
||||
"aliases": [
|
||||
"方向",
|
||||
"横向",
|
||||
"纵向"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "nav-style",
|
||||
"label": "样式",
|
||||
"description": "选项文字的字号、颜色、对齐和选中项的颜色",
|
||||
"fields": [
|
||||
{
|
||||
"path": "style.fontSize",
|
||||
"label": "字号",
|
||||
"description": "选项文字的大小",
|
||||
"value_type": "number",
|
||||
"minimum": 8,
|
||||
"maximum": 100,
|
||||
"unit": "px",
|
||||
"aliases": [
|
||||
"字体大小",
|
||||
"文字大小"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "style.color",
|
||||
"label": "文字颜色",
|
||||
"description": "未选中选项的文字颜色",
|
||||
"value_type": "color",
|
||||
"themed": true,
|
||||
"aliases": [
|
||||
"字体颜色",
|
||||
"颜色"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "style.activeColor",
|
||||
"label": "选中文字色",
|
||||
"description": "选中选项的文字颜色",
|
||||
"value_type": "color",
|
||||
"themed": true,
|
||||
"aliases": [
|
||||
"高亮颜色",
|
||||
"选中颜色"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "style.activeBackground",
|
||||
"label": "选中背景色",
|
||||
"description": "选中选项的背景颜色",
|
||||
"value_type": "color",
|
||||
"themed": true,
|
||||
"aliases": [
|
||||
"高亮背景",
|
||||
"选中底色"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "style.textAlign",
|
||||
"label": "对齐",
|
||||
"description": "选项在组件内的水平位置",
|
||||
"value_type": "enum",
|
||||
"options": [
|
||||
{
|
||||
"value": "left",
|
||||
"label": "左对齐"
|
||||
},
|
||||
{
|
||||
"value": "center",
|
||||
"label": "居中"
|
||||
},
|
||||
{
|
||||
"value": "right",
|
||||
"label": "右对齐"
|
||||
}
|
||||
],
|
||||
"aliases": [
|
||||
"水平对齐"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"event_groups": [
|
||||
"dom-events"
|
||||
],
|
||||
"schema": {
|
||||
"type": "nav",
|
||||
"name": "选项导航",
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 480,
|
||||
"height": 48
|
||||
},
|
||||
"props": {
|
||||
"var": "",
|
||||
"items": [
|
||||
{
|
||||
"label": "选项一",
|
||||
"value": "a"
|
||||
},
|
||||
{
|
||||
"label": "选项二",
|
||||
"value": "b"
|
||||
},
|
||||
{
|
||||
"label": "选项三",
|
||||
"value": "c"
|
||||
}
|
||||
],
|
||||
"direction": "horizontal"
|
||||
},
|
||||
"style": {
|
||||
"fontSize": 16,
|
||||
"textAlign": "center"
|
||||
},
|
||||
"events": []
|
||||
}
|
||||
}
|
||||
@@ -127,44 +127,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-xy"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "pictorial-bar-chart",
|
||||
"name": "象形柱图",
|
||||
|
||||
@@ -25,44 +25,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-item"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "pie-chart",
|
||||
"name": "饼图",
|
||||
|
||||
@@ -67,44 +67,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-item"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "progress-ring-chart",
|
||||
"name": "进度环",
|
||||
|
||||
@@ -148,44 +148,12 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "radar-chart",
|
||||
"name": "雷达图",
|
||||
|
||||
@@ -45,44 +45,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-item"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "ring-chart",
|
||||
"name": "环形图",
|
||||
|
||||
@@ -66,44 +66,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-item"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "rose-chart",
|
||||
"name": "玫瑰图",
|
||||
|
||||
@@ -70,44 +70,12 @@
|
||||
"chart-dataset",
|
||||
"chart-encode-xy"
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "scatter-chart",
|
||||
"name": "散点图",
|
||||
|
||||
@@ -151,44 +151,12 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events",
|
||||
"chart-events"
|
||||
],
|
||||
"rows_path": "props.option.dataset.source",
|
||||
"data_source": true,
|
||||
"schema": {
|
||||
"type": "stacked-bar-chart",
|
||||
"name": "堆叠柱状图",
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
"成本、退款率、投诉量等越低越好的指标把 downIsGood 设为 true,涨跌颜色随之反过来",
|
||||
"数值、对比的变化量和趋势数据可以写 $query:数值和变化量的结果要一行一列,趋势数据只选一列",
|
||||
"迷你趋势线的 trend.data 是按时间先后排列的一组数字;trend.show 设为 true 才显示",
|
||||
"前缀、后缀、说明为空字符串时不显示;对比用 show 控制是否显示"
|
||||
"前缀、后缀、说明为空字符串时不显示;对比用 show 控制是否显示",
|
||||
"明细指标卡:rows 写按筛选字段汇总好的数据行(可写 $query),bind 把列绑定到字段,例如 {\"value\": \"calls\", \"compare.value\": \"calls_mom\", \"trend.data\": \"trend\"};先按 filters 筛选,取第一行按 bind 覆盖对应字段",
|
||||
"rows 里每个筛选值只对应一行;趋势用数组列,例如 DuckDB 的 list(calls ORDER BY day) AS trend"
|
||||
],
|
||||
"field_groups": [
|
||||
"node-base",
|
||||
@@ -488,46 +490,42 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "stat-rows",
|
||||
"label": "数据行",
|
||||
"description": "按数据行显示指标:rows 是数据行,bind 把列绑定到指标卡字段,配合 filters 按变量取对应的一行",
|
||||
"fields": [
|
||||
{
|
||||
"path": "props.rows",
|
||||
"label": "数据行",
|
||||
"description": "对象数组,每行一组指标值,按 filters 的字段汇总好,每个筛选值一行;可以写 $query",
|
||||
"value_type": "array",
|
||||
"item_type": "object",
|
||||
"panel": false,
|
||||
"aliases": [
|
||||
"明细数据",
|
||||
"行数据"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "props.bind",
|
||||
"label": "列绑定",
|
||||
"description": "键是 props 下的字段路径(value、compare.value、compare2.value、trend.data 等),值是数据行的列名;有数据行时取筛选后的第一行,按它覆盖这些字段",
|
||||
"value_type": "object",
|
||||
"panel": false,
|
||||
"aliases": [
|
||||
"绑定",
|
||||
"字段绑定"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events"
|
||||
],
|
||||
"rows_path": "props.rows",
|
||||
"schema": {
|
||||
"type": "stat-card",
|
||||
"name": "指标卡",
|
||||
@@ -565,7 +563,9 @@
|
||||
"type": "area",
|
||||
"position": "right",
|
||||
"data": []
|
||||
}
|
||||
},
|
||||
"rows": [],
|
||||
"bind": {}
|
||||
},
|
||||
"style": {
|
||||
"textAlign": "left",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
{
|
||||
"path": "props.content",
|
||||
"label": "内容",
|
||||
"description": "文本组件显示的文字",
|
||||
"description": "文本组件显示的文字;可以写 {{变量ID}} 引用大屏变量,例如“{{model}} 调用明细”,变量变化时文字跟着变",
|
||||
"value_type": "string",
|
||||
"aliases": [
|
||||
"文字",
|
||||
@@ -34,7 +34,8 @@
|
||||
"名字"
|
||||
],
|
||||
"example": "label",
|
||||
"identifies": true
|
||||
"identifies": true,
|
||||
"template": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -147,43 +148,8 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"eventOptions": [
|
||||
{
|
||||
"label": "点击事件",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"label": "双击事件",
|
||||
"value": "dblclick"
|
||||
},
|
||||
{
|
||||
"label": "鼠标进入事件",
|
||||
"value": "mouseenter"
|
||||
},
|
||||
{
|
||||
"label": "鼠标离开事件",
|
||||
"value": "mouseleave"
|
||||
},
|
||||
{
|
||||
"label": "鼠标按下事件",
|
||||
"value": "mousedown"
|
||||
},
|
||||
{
|
||||
"label": "鼠标抬起事件",
|
||||
"value": "mouseup"
|
||||
},
|
||||
{
|
||||
"label": "鼠标移动事件",
|
||||
"value": "mousemove"
|
||||
},
|
||||
{
|
||||
"label": "鼠标滚轮事件",
|
||||
"value": "mousewheel"
|
||||
},
|
||||
{
|
||||
"label": "生命周期事件",
|
||||
"value": "vnodeMounted"
|
||||
}
|
||||
"event_groups": [
|
||||
"dom-events"
|
||||
],
|
||||
"schema": {
|
||||
"type": "text",
|
||||
@@ -202,13 +168,6 @@
|
||||
"textAlign": "center",
|
||||
"alignItems": "center"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"type": "click",
|
||||
"title": "点击事件",
|
||||
"name": "fn",
|
||||
"code": "console.log(123)"
|
||||
}
|
||||
]
|
||||
"events": []
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user