# 数据交互选项（筛选 / 选中联动 / 动态数据项 / 条件样式 / 属性数据源）

> `VpElement` 基类的数据交互能力，多数组件可用；**持久化形态写错会导入报错**（尤其动态数据项的 JSON 串键）。
> 返回目录：[index.md](index.md)；全量选项字典（最权威）：[widget-schema.generated.json](../widget-schema.generated.json)。

## 0. 数据交互选项详解（筛选 / 选中 / 联动 / 动态数据项 / 条件样式）

这些是 `VpElement` 基类能力（多数组件可用），是"数据交互"类需求的核心。**务必按下面的持久化形态写**，写错会导入即报错（尤其动态数据项）。每个特性都有对应的可导入示例：见 [examples/](../../examples/) 下的 `filter.json`、`selection-passing.json`、`linkage.json`、`dynamic-data-item.json`、`conditional-style.json`、`secondary-dev-selection.json`。

### 0.1 数据筛选 filter（组件级，先筛后用）

`filter:true` 开启，再用 `row-filter-mode` 选模式：

| row-filter-mode | 含义 | 配套参数 |
|---|---|---|
| `no-condition`(默认) | 不筛 | — |
| `data-row` | 只取第 N 行 | `row-filter-row-index`（从 1 起） |
| `data-rows` | 从第 N 行起取 M 条 | `row-filter-start-index`（起始,1 起）、`row-filter-row-count`（条数） |
| `custom-rules` | 条件构建器（内联条件组逐行过滤） | `row-filter-rules`（二维数组，组间 OR/组内 AND，与 `dataRules[].rules` 同构） |
| `data-rule` | 满足某工程数据规则的行 | `row-row-filter-mode-id` = `dataRules[].uid`（⚠️键名就是这么拗口，不是 `row-filter-rule`） |
| `direct-judgment` | 直接按「表-字段-运算符-值」判断 | `row-filter-source-table`(`"connUid,tableUid"`)、`row-filter-source-field`、`row-filter-operator`(`=`/`>`/`<`/`!=`/`>=`/`<=`/`⊃`/`!⊃`/`∅`/`!∅`)、`row-filter-compare-value`（`∅`/`!∅` 不需要） |

```json
"options": { "filter": true, "row-filter-mode": "data-rows",
             "row-filter-start-index": 2, "row-filter-row-count": 3 }
```

另有**渲染限流 `data-row-limit`**（number，默认 0=不限）：组件渲染行数上限，只截断渲染取数、不影响条件样式判断与字段统计，大数据量保护性能用。

### 0.2 选中传递 selection / 联动 linkage

- 发起方开 `select-sync-emit:true`（或 `cross-filter-emit:true`）；接收方开 `select-sync-receive:true`（或 `cross-filter-receive:true`）。cross-filter 只看**接收组件自身**开关，页面开关可省。选中传递：`basic-table`（及 `map-2d`）自带 `onSelection`，自身 `select-sync-receive` 即可；**图表/列表/排行等其余接收方只实现 `onSelectItem`，要靠父容器（页面或 `group-panel`）开 `select-sync-receive` 向子级广播**——而页面级开关只在编辑器内预览生效（运行态 `/view`、SDK 页面不初始化 `watchSelection`，见 [interactions.md](../interactions.md) 触发器清单下注），故运行态须把这类接收方包进开了 `select-sync-receive` 的 `group-panel`。
- 表格用 `select-sync-fields`（field，三元组）声明「按哪个字段匹配选中行」；图表按其类目维度（`category` / `category-field`）发起。
- 联动广播的键是字段**别名**，接收侧按「别名 → uid → sourceField」解析回行键；组件取数顺序是**先联动过滤、再组件筛选**（§0.1）。
- 高级路由（精确控制作用范围，默认广播给所有开接收的组件）：`cross-filter-routing-enabled:true` + `cross-filter-routing`——按方向分组，键为 `cross-filter-emit`/`cross-filter-receive`，每组 `{ "cross-filter-list-mode": "blacklist"|"whitelist", "cross-filter-widgets": [目标元素引用] }`。选中传递同构：`select-sync-routing-enabled` + `select-sync-routing`（内层键 `select-sync-list-mode`/`select-sync-widgets`）。

```json
// 饼图发起
"options": { "select-sync-emit": true, "category": [["conn","tbl","城市"]], "value": [["conn","tbl","数量"]] }
// 表格接收
"options": { "select-sync-receive": true, "select-sync-fields": [["conn","tbl","城市"]], "table-data-fields": [["conn","tbl","城市"]] }
```

### 0.3 动态数据项 dynamic-prop-fields（用数据驱动任意样式项）⚠️ 最易错

把某个**样式选项**（如 `opacity`）绑到数据字段，数据变样式跟着变。`dynamic-prop-entries[].name` 与 `dynamic-prop-fields` 的**键**都必须是「选项路径数组」的 **JSON 字符串**（运行时会 `JSON.parse(name)`）：

- `dynamic-prop-entries`: `[{ "name": "<JSON路径>", "alias": "显示名" }]`
- `dynamic-prop-fields`: `{ "<JSON路径>": { "dynamic-prop-field": [[connUid,tableUid,fieldUid]] } }`
- `<JSON路径>` = `JSON.stringify(["opacity"])` 得到的字符串 `"[\"opacity\"]"`（**不是**裸 `"opacity"`！多段路径同理 `JSON.stringify(["a","b"])`）。

```json
"options": {
  "dynamic-prop-entries": [{ "name": "[\"opacity\"]", "alias": "不透明度" }],
  "dynamic-prop-fields": { "[\"opacity\"]": { "dynamic-prop-field": [["conn","tbl","不透明度"]] } }
}
```

> 裸写 `"opacity"` 会在运行时抛 `Unexpected token 'o', "opacity" is not valid JSON`。`opacity`、`enable` 两路径在编辑态也即时应用，其它路径运行/预览态才写入。源码：`VpElement.watchRelatedFields`（`JSON.parse(entry.name)`）、`_toRelatedFieldEntry`（`JSON.stringify([paths])`）。

### 0.4 条件样式 conditionalStyles（满足数据规则→改样式）

**写在节点顶层（与 `options` 平级），不在 `options` 里**。命中工程数据规则就把 `value` 应用到 `paths`：

```json
{
  "uid": "w_x", "type": "basic-text",
  "options": { "text-font": { "size": 64, "color": "#ffffff" } },
  "conditionalStyles": [{
    "paths": ["text-font"],
    "cases": [
      { "ruleId": "dc_alarm", "value": { "size": 64, "color": "#ff4d4f" } },
      { "fallback": true, "value": { "size": 64, "color": "#52c41a" } }
    ]
  }]
}
```

- `paths`：目标选项路径（字符串数组，**直接用**，不做 JSON 编码）。
- `cases` 按顺序求值：`ruleId` 引用 `dataRules[].uid`；或 `inlineRules`（二维规则，形同 `dataRule.rules`）；`fallback:true` 兜底。命中即应用其 `value` 并停止。
- 命中写值走 `setTransientOption`（不进历史、退预览还原）；无命中且无 `fallback` 时 `unsetTransientOption` 还原基线值。`getOption()` 会合并 transient 层，可直接读回生效值。
- 程序化 API：`element.setConditionalStyle(paths, ruleObj)` / `unsetConditionalStyle(paths)` / `getConditionalStyle(paths)`，求值器 `element._resolveConditionalStyleValue(ruleObj)` 返回命中值/兜底/`undefined`。`inlineRules` 每项是 `[[{field,func,args}]]` 组数组（组内 AND、组间 OR）；`field` 用字段 uid，`connection`/`table` 可省（按 field uid 反查所属表）。

> 二次开发触发选中：按钮内 `applySelection({字段:值})` / `withdrawSelection()` 属**宿主二次开发代码**（`ExecuteScriptAction` 经 `runHostScript` 委托宿主执行），**不在工程 JSON 内**。本组件集无「按钮/横向菜单」，工程 JSON 层面用「支持选中传递的中间组件（如饼图）→ 表格 select-sync-fields」等价复现最终效果（见 `secondary-dev-selection.json`）。

### 0.5 属性数据源 diy-connection（组件属性反向产出连接，供别组件绑定）

编辑器里某属性右侧 ⋮ →「生成属性数据源」即可把该属性（如文本的 `text`）暴露成一条数据连接，其它组件在「数据」面板的页面公共数据树里就能勾选绑定。产物会真正并入工程 `connections` / `connectionData`（数据树可见、可绑、随工程持久化）。一份导入快照需缀合三处（约定：`<cUID>`=生产方组件 uid（piece 为 `ownerWidget.uid-uid`）；`<entry>`=该属性选项路径的 JSON 串，如 `["text"]`；连接 uid 恒为 `__vp_prop_<cUID>`、表 uid 恒为 `<cUID>::<entry>`、字段恒为 `value`）：

```jsonc
// ① 生产方节点 options（与「生成」后持久化的一致）
{ "uid": "w_src", "type": "basic-text",
  "options": {
    "text": "本月主推：新能源汽车",
    "prop-source-entries": [
      { "name": "[\"text\"]", "alias": "主推文案", "cUID": "w_src", "connectionUID": "__vp_prop_w_src" }
    ]
  } }
// ② connections 增加一条 prop-source 连接
{ "uid": "__vp_prop_w_src", "name": "属性数据源", "type": "prop-source", "connectorType": "prop-source",
  "tables": [{ "uid": "w_src::[\"text\"]", "name": "主推文案", "fields": [{ "uid": "value", "alias": "value", "type": "string" }] }] }
// ③ connectionData 灌入当前值（运行时由生产方 watch 持续回写）
"__vp_prop_w_src": [{ "tableId": "w_src::[\"text\"]", "rows": [{ "value": "本月主推：新能源汽车" }] }]
// 消费方按三元组绑定：文本 value-field / 表格 table-data-fields
"value-field": [["__vp_prop_w_src", "w_src::[\"text\"]", "value"]]
```

- `prop-source-entries[].name` 与表 uid 的 `<entry>` 是**选项路径数组的 JSON 串**（`JSON.stringify(["text"])`），不是裸字符串。
- 编辑器「生成属性数据源」只产出单字段 `value`（无绑定字段路径）；消费方字段 uid 恒为 `value`。
- 源码：`VpElement.addDiyConnectionField/watchDiyConnectionFields/removeDiyConnectionField`（产出/监听/移除）+ `VpScreen.createPropertyConnection/updatePropertyConnectionData/updateDiyData/removePropertyConnection`（挂在 Screen 上、委托 `projectContext`；并入工程连接、回写行、移除）。完整示例见 `examples/property-data-source.json`。

---

## VP-TSX 写法（本文特性的映射要点）

映射总规则见 [vptsx.md](../vptsx.md)。本文四处易错点在 TSX 里长这样：

```tsx
// 筛选/选中联动：普通 kebab 属性
<basic-table select-sync-receive={true} select-sync-fields={bind('conn', 'tbl', '城市')} … />

// 动态数据项：键仍是「选项路径数组的 JSON 串」——TSX 里不豁免，照写转义串或用 JSON.stringify
<basic-graphical
  dynamic-prop-entries={[{ name: JSON.stringify(['opacity']), alias: '不透明度' }]}
  dynamic-prop-fields={{ [JSON.stringify(['opacity'])]: { 'dynamic-prop-field': bind('conn', 'tbl', '不透明度') } }} />

// 条件样式：顶层保留 prop（不进 options），ruleId 命中 defineProject 的 dataRules[].uid
<basic-text conditionalStyles={[{ paths: ['text-font'], cases: [{ ruleId: 'dc_alarm', value: { size: 40, color: '#ff4d4f' } }] }]} … />

// 属性数据源：三处缀合与 JSON 完全同构（prop-source-entries 属性 + connections/connectionData 原始对象）
```
