@qingflow-tech/qingflow-cli 1.1.61-beta.5 → 1.1.61-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +1 -1
- package/pyproject.toml +2 -1
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md +1 -1
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md +6 -258
- package/skills/qingflow-cli/reference/builder/70-portal.md +31 -16
- package/src/qingflow_mcp/builder_facade/models.py +27 -4
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/complete.json +7 -7
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/carousel.json +1 -1
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/chart.json +1 -1
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/favourite.json +1 -1
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/filter.json +45 -64
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/grid.json +1 -1
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/link.json +1 -1
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/task.json +2 -2
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/text.json +1 -1
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/view.json +1 -1
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/summary.json +4 -4
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/complete.json +1 -1
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/display.json +1 -1
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/summary.json +1 -1
- package/src/qingflow_mcp/builder_facade/service.py +158 -28
- package/src/qingflow_mcp/cli/commands/builder.py +20 -2
- package/src/qingflow_mcp/server_app_builder.py +2 -2
- package/src/qingflow_mcp/tools/ai_builder_tools.py +8 -0
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Qingflow CLI 是轻流面向开发者和 AI Agent 的命令行工具。它把轻
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @qingflow-tech/qingflow-cli@1.1.61-beta.
|
|
10
|
+
npm install @qingflow-tech/qingflow-cli@1.1.61-beta.7
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
推荐从官网复制这一条一键安装 Qingflow CLI:
|
|
@@ -21,7 +21,7 @@ npx qingflow-cli@latest install
|
|
|
21
21
|
## 运行
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
npx -y -p @qingflow-tech/qingflow-cli@1.1.61-beta.
|
|
24
|
+
npx -y -p @qingflow-tech/qingflow-cli@1.1.61-beta.7 qingflow
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
全局安装后可以直接运行:
|
|
@@ -62,7 +62,7 @@ qingflow-skills install --agent codex --scope user
|
|
|
62
62
|
如果你通过一次性 `npx -p` 执行 skills 安装,建议加 `--copy`,避免 symlink 指向 npm 临时执行缓存:
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
|
-
npx -y -p @qingflow-tech/qingflow-cli@1.1.61-beta.
|
|
65
|
+
npx -y -p @qingflow-tech/qingflow-cli@1.1.61-beta.7 qingflow-skills install --agent codex --scope user --copy
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
`qingflow-skills install` 默认使用 symlink,且遇到同名 skill 不会覆盖;只有显式传入 `--force` 才会替换。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qingflow-tech/qingflow-cli",
|
|
3
|
-
"version": "1.1.61-beta.
|
|
3
|
+
"version": "1.1.61-beta.7",
|
|
4
4
|
"description": "Human-friendly Qingflow command line interface for auth, record operations, import, tasks, and stable builder flows.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "qingflow-mcp"
|
|
7
|
-
version = "1.1.
|
|
7
|
+
version = "1.1.61b7"
|
|
8
8
|
description = "User-authenticated MCP server for Qingflow"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -70,6 +70,7 @@ qingflow_mcp = [
|
|
|
70
70
|
"py.typed",
|
|
71
71
|
"builder_facade/schemas/app_form/**/*.json",
|
|
72
72
|
"builder_facade/schemas/views/**/*.json",
|
|
73
|
+
"builder_facade/schemas/portals/**/*.json",
|
|
73
74
|
]
|
|
74
75
|
|
|
75
76
|
[tool.pytest.ini_options]
|
|
@@ -233,5 +233,5 @@ CLI 对外推荐使用 **`group_by` + `metric(s)`**;旧的 **`dimension_field_
|
|
|
233
233
|
|
|
234
234
|
- [SKILL.md](../SKILL.md)
|
|
235
235
|
- [QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md](./QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md)
|
|
236
|
-
- [
|
|
236
|
+
- [builder/70-portal.md](./builder/70-portal.md)(门户声明式主链与 **`chart` / `chart_ref`** 用法)
|
|
237
237
|
- [QINGFLOW_CLI_FIELD_DATA_TYPES.md](./QINGFLOW_CLI_FIELD_DATA_TYPES.md)(成员侧 **`kind`** 与搭建 **`type`** 对照理解业务)
|
|
@@ -1,263 +1,11 @@
|
|
|
1
|
-
# Builder
|
|
1
|
+
# Builder Portal Workflow
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
实现要点来自 CLI 打包内的 `builder_facade/models.py`(`PortalApplyRequest`、`PortalSectionPatch`)、`builder_facade/service.py`(`portal_apply` / `portal_delete`)、`cli/commands/builder.py`。已对 **`package_id=2030703`**、应用 **`ead8ims5i401`** **实跑**:**新建**(`--no-publish`)、**`--publish` 更新**(网关 **503** 恢复后已重试成功)、**五类区块可稳定落库**;**`filter`(`type:6`)经 CLI 直传未落库** 见 **§3.4 / §8**。另已对测试包 **`1414907`** 实跑 **`portal delete`**:临时门户 **`etcivtmv5402`** 删除后回读 **`readback_status=deleted`**。
|
|
6
|
-
|
|
7
|
-
> **权限**:**更新**需 **`edit_portal`**;**新建**按后端 `DashCtrl.createDash` 链路只预检目标包 **`add_app`**,不额外要求包 **`edit_app`**。失败时参见主技能与 **ADMIN** 速查。
|
|
8
|
-
|
|
9
|
-
> **契约**:`qingflow --json builder contract --tool-name portal_apply`;删除契约:`qingflow --json builder contract --tool-name portal_delete`
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## 1. 读:`list` → `get`
|
|
3
|
+
旧文已并入 [builder/70-portal.md](./builder/70-portal.md)。门户创建/更新走声明式主链(版本化 `--file` 声明 + 离线 validate + 唯一 apply 入口):
|
|
14
4
|
|
|
15
5
|
```bash
|
|
16
|
-
qingflow --json builder portal
|
|
17
|
-
|
|
18
|
-
qingflow
|
|
19
|
-
qingflow --json builder portal get --dash-key "<DASH_KEY>" --no-being-draft
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
- **`list`** 可能带 **`PORTAL_PERMISSION_READ_UNAVAILABLE`**,**`verified: false`** 仍可能有可用 **`items[]`**。
|
|
23
|
-
- **`get`**:**`--dash-key`** 必填;默认 **`--being-draft`** 为草稿。
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## 2. 写:`apply` 两种模式(互斥)
|
|
28
|
-
|
|
29
|
-
**不可同时**出现 **`--dash-key`** 与 **`--package-id`**。
|
|
30
|
-
|
|
31
|
-
| 模式 | 必备参数 | 含义 |
|
|
32
|
-
|------|----------|------|
|
|
33
|
-
| **新建** | **`--package-id`** + **`--dash-name`** | **`--sections-file` 须非空**。 |
|
|
34
|
-
| **更新** | **`--dash-key`** | 不要带 **`package_id`**。 |
|
|
35
|
-
|
|
36
|
-
| 参数 | 说明 |
|
|
37
|
-
|------|------|
|
|
38
|
-
| **`--publish` / `--no-publish`** | 默认 **`--publish`**。仅 **`--no-publish`** 时不会调用发布接口;契约说明 **`publish=false` 不宣称线上已变**。 |
|
|
39
|
-
| **`--payload-file`** | 完整门户 JSON 对象;**标准字段写 `dash_name`**。新版 CLI 兼容 **`name -> dash_name`**、单页 **`pages[0].components -> sections`**,但 agent 默认不要依赖兼容别名。 |
|
|
40
|
-
| **`--sections-file`** | JSON **数组**;**全量替换**。省略时仅改基础信息,且 **`hide_copyright` / `dash_global_config` / `config`** 不能单独提交(**`PORTAL_SECTIONS_REQUIRED`**)。 |
|
|
41
|
-
| **`--layout-preset`** | 可选:**`auto` / `dashboard_2col` / `dashboard_3col`**。当区块未显式写 **`position`** 时由工具生成大屏布局。 |
|
|
42
|
-
| **`--visibility-file`** | 与 **`--auth-file`** 不能同时用。 |
|
|
43
|
-
| **`--icon`** / **`--color`** | 新建门户必须显式传合法的非 `template` 图标和颜色;候选用 `qingflow --json builder icon catalog`。编辑已有门户时省略则保留现状。 |
|
|
44
|
-
| **`--hide-copyright`** / **`--dash-global-config-file`** / **`--config-file`** | 见契约;部分键 **依赖与 `sections` 同批提交**。 |
|
|
45
|
-
|
|
46
|
-
**图标规则**:CLI 不按门户名自动猜图标;智能体需要自行选择业务贴合的 `icon + color`。读回结果里的 `icon_config` 可直接用于前端资源卡片展示。
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## 3. `sections[]`:六类组件与回读 `type`
|
|
51
|
-
|
|
52
|
-
### 3.1 公共键
|
|
53
|
-
|
|
54
|
-
- **`title`**(必填)、**`source_type`**(必填,小写)、可选 **`position`**(**`pc`/`mobile`** 各 **`x,y,cols,rows`**;省略则由**实现自动排布**)。
|
|
55
|
-
- **`config`**、**`dash_style_config`**:按类型 Optional。默认只写 snake_case 规范键。
|
|
56
|
-
|
|
57
|
-
### 3.1.0 Payload 字段口径
|
|
58
|
-
|
|
59
|
-
推荐完整 payload 直接使用 CLI 标准键:
|
|
60
|
-
|
|
61
|
-
```json
|
|
62
|
-
{
|
|
63
|
-
"dash_name": "产品研发数据大屏",
|
|
64
|
-
"package_id": 1414909,
|
|
65
|
-
"layout_preset": "dashboard_2col",
|
|
66
|
-
"pages": [
|
|
67
|
-
{
|
|
68
|
-
"title": "研发总览",
|
|
69
|
-
"components": [
|
|
70
|
-
{
|
|
71
|
-
"title": "部门分布",
|
|
72
|
-
"source_type": "chart",
|
|
73
|
-
"chart_ref": {"app_key": "APP_KEY", "chart_id": "CHART_ID"}
|
|
74
|
-
}
|
|
75
|
-
]
|
|
76
|
-
}
|
|
77
|
-
]
|
|
78
|
-
}
|
|
6
|
+
qingflow --json builder portal get --dash-key DASH_KEY --schema-kind complete > portal.json # 更新前先读
|
|
7
|
+
qingflow --json builder portal validate --file portal.json # 零后端离线校验
|
|
8
|
+
qingflow builder portal apply --file portal.json --schema-version v1alpha1 [--dash-key DASH_KEY] [--no-publish]
|
|
79
9
|
```
|
|
80
10
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
### 3.1.1 布局硬规则:PC 24 栅格,mobile 6 栅格
|
|
84
|
-
|
|
85
|
-
**门户 PC 端不是 12 栅格,而是 24 栅格。**如果把两列写成 **`x=0/6, cols=6`**,所有组件只会占左半屏,工具会返回 **`PORTAL_LAYOUT_HALF_WIDTH`**。
|
|
86
|
-
|
|
87
|
-
推荐:
|
|
88
|
-
|
|
89
|
-
| 场景 | PC 写法 |
|
|
90
|
-
|------|---------|
|
|
91
|
-
| 不确定布局 | **省略 `position`**,或传 **`--layout-preset auto`** |
|
|
92
|
-
| 两列可视化图表 | **`x=0/12, cols=12`**,图表 **`rows >= 7`** |
|
|
93
|
-
| 三列可视化图表 | **`x=0/8/16, cols=8`**,图表 **`rows >= 7`** |
|
|
94
|
-
| 四个指标卡 | **`x=0/6/12/18, cols=6, rows=5`** |
|
|
95
|
-
| 具体数据视图 / 明细表 | 优先 **`cols=12`** 或 **`cols=24`**,视图 **`rows >= 11`** |
|
|
96
|
-
|
|
97
|
-
mobile 写法固定按 **6 栅格**:通常 **`x=0, cols=6`**。如果只写了 PC position,CLI 会自动补 mobile position,并返回 **`PORTAL_MOBILE_POSITION_MISSING`** 提醒。
|
|
98
|
-
|
|
99
|
-
图表卡片过小时会返回 **`PORTAL_CHART_CARD_TOO_SMALL`**;指标卡按 **`pc.cols >= 6, pc.rows >= 5`** 校验,普通可视化图表按 **`pc.cols >= 8, pc.rows >= 7`** 校验。指标区图表建议写 `role: "metric"`;此时必须引用 `target` / `indicator` 图表,否则返回 **`PORTAL_METRIC_SECTION_CHART_TYPE_MISMATCH`**,应先用 `app_charts_apply` 创建缺失指标卡再组门户。标准工作台数量也会进入 `layout_diagnostics.standard_template_counts`:指标卡推荐 4-6 个、BI 图表 2-3 个、业务视图 1-2 个,超出或不足会返回 `PORTAL_STANDARD_*_COUNT_OUT_OF_RANGE`。
|
|
100
|
-
|
|
101
|
-
### 3.1.2 推荐门户模板:业务工作台
|
|
102
|
-
|
|
103
|
-
默认按 **业务入口 → 核心指标 → BI 可视化 → 业务视图** 搭建。门户首屏应直接呈现可用工作台,不要做营销页、说明页或大面积装饰区。
|
|
104
|
-
|
|
105
|
-
| 区域 | 推荐组件 | PC 布局 | 要点 |
|
|
106
|
-
|------|----------|---------|------|
|
|
107
|
-
| 顶部业务入口 | **`grid`** + 可选 **待办 `task`** | `grid x=0,y=0,cols=12,rows=4`;待办 `x=12,y=0,cols=12,rows=4` | `grid` 放 2-6 个核心业务入口;待办用于当前用户任务概览。当前公开 `portal apply --sections-file` 暂未支持 `source_type=task`,不要伪造;见下方边界说明。 |
|
|
108
|
-
| 核心指标 | **`chart`** 指标卡 + `role: "metric"` | 4 张:`x=0/6/12/18,y=4,cols=6,rows=5` | 指标卡推荐高度 **5**。24 栅格下单卡最小 `cols=6`,所以一行 4 张最稳;如果要 5 个指标,拆成两行。 |
|
|
109
|
-
| BI 可视化 | **`chart`** 图表 | 3 张:`x=0/8/16,y=9,cols=8,rows=7`;或 2 张:`x=0/12,cols=12,rows=7` | 可视化图表推荐高度 **7**,一行 2-3 个,1-2 行。 |
|
|
110
|
-
| 业务数据视图 | **`view`** | `x=0/12,y=16,cols=12,rows=11`;或单表 `cols=24,rows=11` | 数据视图推荐高度 **11**,一行 1-2 个,1-2 行。只挂业务视图,不要创建或引用默认的 **全部数据 / 我的数据** 当主门户视图。 |
|
|
111
|
-
|
|
112
|
-
mobile 固定按 **6 栅格** 从上到下堆叠:通常所有组件 `x=0, cols=6`,`y` 按 PC 顺序递增。若顶部同时有 `grid + task`,mobile 建议先业务入口 `y=0,rows=4`,再待办 `y=4,rows=4`,核心指标从 `y=8` 开始;若当前公开 CLI 只能写 `grid`,核心指标可从 `y=4` 开始。
|
|
113
|
-
|
|
114
|
-
**当前工具边界**:
|
|
115
|
-
|
|
116
|
-
- 公开 `sections-file` 稳定支持:**`grid` / `chart` / `view` / `text` / `link`**;`filter` 仍按 **§3.4** 的 raw `filterConfig` 边界处理,不作为主路径。
|
|
117
|
-
- **待办 `task` / 常用 `favorite`** 是前端/后端 raw 组件形状,不在当前公开 `source_type` 合约里。智能体不要在 `sections-file` 中写 `source_type: "task"`;只有工具后续显式支持 `task`,或维护者走 raw 门户写入链路时,才使用待办槽位。
|
|
118
|
-
- 推荐门户的可直接 apply 示例见 **[portal_sections_standard_workbench.example.json](./portal_sections_standard_workbench.example.json)**。该示例使用当前公开 CLI 可稳定写入的组件,顶部先用一个业务入口 `grid` 占满 24 栅格;如果工具已支持待办组件,再把顶部拆成 `grid cols=12 + task cols=12`。
|
|
119
|
-
- `grid` 必须写 **`config.items[]`**;只写 `gridTitle` / `beingShowTitle` 会生成空入口容器,工具会返回 **`PORTAL_GRID_ITEMS_EMPTY`**。
|
|
120
|
-
|
|
121
|
-
**待办组件 raw 形状参考**(仅用于工具实现对齐,当前不要作为 `--sections-file` 输入):
|
|
122
|
-
|
|
123
|
-
```json
|
|
124
|
-
{
|
|
125
|
-
"type": 8,
|
|
126
|
-
"position": {
|
|
127
|
-
"pc": {"x": 12, "y": 0, "cols": 12, "rows": 4},
|
|
128
|
-
"mobile": {"x": 0, "y": 4, "cols": 6, "rows": 4}
|
|
129
|
-
},
|
|
130
|
-
"taskConfig": {
|
|
131
|
-
"beingShowTitle": true,
|
|
132
|
-
"componentTaskTitle": "待办",
|
|
133
|
-
"beingShowHint": true,
|
|
134
|
-
"componentTaskHint": "及时处理待办,可以有效提升流程效率",
|
|
135
|
-
"dashTaskConfigList": [
|
|
136
|
-
{"type": 1, "title": "all.taskTodo", "beingCheck": true, "ordinal": 0},
|
|
137
|
-
{"type": 2, "title": "all.taskTimeout", "beingCheck": true, "ordinal": 1},
|
|
138
|
-
{"type": 3, "title": "all.taskUpcomingTimeout", "beingCheck": true, "ordinal": 2},
|
|
139
|
-
{"type": 4, "title": "all.taskRemind", "beingCheck": true, "ordinal": 3}
|
|
140
|
-
]
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### 3.2 `source_type` ↔ 回读 `components[].type`(搭建侧)
|
|
146
|
-
|
|
147
|
-
| `source_type` | 回读 `type`(数字) | 搭建侧含义 |
|
|
148
|
-
|---------------|---------------------|------------|
|
|
149
|
-
| **`chart`** | **9** | QingBI 图表块 |
|
|
150
|
-
| **`view`** | **10** | 应用视图块 |
|
|
151
|
-
| **`grid`** | **2** | 九宫格 |
|
|
152
|
-
| **`filter`** | **6** | 筛选条 |
|
|
153
|
-
| **`text`** | **5** | 富文本说明 |
|
|
154
|
-
| **`link`** | **4** | 外链 |
|
|
155
|
-
|
|
156
|
-
CLI 校验(`PortalSectionPatch`):**chart** 须 **`chart_ref`**;**view** 须 **`view_ref`**;**text** 须 **`text`**;**link** 须 **`url`**。**grid** / **filter** 无额外 ref,**`config` 形状由后端接受为准**。
|
|
157
|
-
|
|
158
|
-
### 3.3 **`chart`**:几乎总须 **`builder charts apply` 先建报表**
|
|
159
|
-
|
|
160
|
-
- `portal` 只做 **`chart_ref` 解析**(`app_key` + **`chart_id`** 优先,否则 **唯一 `chart_name`**)。
|
|
161
|
-
- 若 **`builder app get charts`** 为空,先 **`builder charts apply --app-key … --upsert-file`** 创建(契约 **minimal_example** 如 **`target` + `indicator_field_ids: []`**),从响应 **`chart_results[].chart_id`** 取 id 再写 **`chart_ref`**.
|
|
162
|
-
|
|
163
|
-
### 3.4 **`filter`**:`config` **整包**即接口里的 **`filterConfig`**
|
|
164
|
-
|
|
165
|
-
实现:组件为 **`{"type": 6, "filterConfig": deepcopy(section.config)}`**。
|
|
166
|
-
|
|
167
|
-
- **理论上**与 solution 编译器一致:外层 **`{"filterConfig": [ … ], "graphList": [ … ]}`**;**`graphList`** 常含 **`graphType`**(如 **`CHART`**)、**`graphKey`** 或 **`graphRef`(`entity_id` / `chart_id`)** 等。
|
|
168
|
-
- **本环境 CLI 直写实测(多次 `--publish`)**:在已验证 **chart / view / grid / text / link** 均可落库的前提下,**`source_type: filter` 始终未出现在 `draft_result.components` 中**;尝试过:**空数组**、**仅 `graphList` + QingBI `chart_id`**、**`graphKey` 为门户 `dashChartId`**、**`chart` 排在 **`filter` 前**、**仅 2 块(chart+filter)** 等,回读仍 **只有 chart(`count: 1`)** 或 **五类缺 `type: 6`**。
|
|
169
|
-
**结论**:当前宜 **在搭建界面创建筛选条**,再 **`builder portal get` 反抓** 原始 `filterConfig` 形状;或向后端确认 **POST `/dash/{dashKey}`** 对 **`type: 6`** 的必填字段。**自动化不要默认认为 `filter` 已写入。**
|
|
170
|
-
- portal `filter` 组件仍按 raw `filterConfig` 处理,不纳入视图 / 报表 / 关联资源的统一筛选 DSL 主链路。
|
|
171
|
-
|
|
172
|
-
### 3.5 **`grid` / `text` / `link` / `view`**
|
|
173
|
-
|
|
174
|
-
- **grid**:**`config`** 并入 **`gridConfig`**;业务入口必须写 **`config.items[]`**。应用入口项推荐 `{ "type": 1, "jumpMode": 1, "linkAppKey": "APP_KEY", "linkFormType": 1, "title": "入口名" }`。空 `config` 或空 `items` 只会生成空入口容器,并触发 **`PORTAL_GRID_ITEMS_EMPTY`**。
|
|
175
|
-
- **text** / **link**:**`text`** / **`url`** 必填。
|
|
176
|
-
- **view**:**`view_ref`**;**`view_key`** 来自 **`builder app get views`**。
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## 4. 前置数据
|
|
181
|
-
|
|
182
|
-
| 目的 | 命令 |
|
|
183
|
-
|------|------|
|
|
184
|
-
| **`package_id`** | **`builder app resolve --app-key …`** → **`package_ids`** |
|
|
185
|
-
| **`view_key`** | **`builder app get --app-key … views`** |
|
|
186
|
-
| **`chart_id`** | **`builder app get charts`** 或 **`charts apply` 创建** |
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
## 5. 新建最小例(`--no-publish`)
|
|
191
|
-
|
|
192
|
-
见上文 **§2** 与 **`visibility`** 最小对象;**`sections`** 可先用 **单块 `view`**。成功后拿 **`dash_key`** 再 **更新 / 发布**。
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
## 6. 更新与替换语义
|
|
197
|
-
|
|
198
|
-
- **`--dash-key`** + **`--sections-file`**:**列表即全量**;没写进去的区块会被拿掉。
|
|
199
|
-
- 仅改 **可见性 / 图标 / 分组**:**不传 `--sections-file`**,且不要单独带 **§2** 所列「仅在有 sections 时允许」的键。
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
## 7. 删除:`delete`
|
|
204
|
-
|
|
205
|
-
```bash
|
|
206
|
-
qingflow --json builder portal delete --dash-key "<DASH_KEY>" > tmp/builder_portal_delete.json
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
- **只按 `dash_key` 删除一个门户**;先用 `builder portal list/get` 确认目标,不要按名称猜。
|
|
210
|
-
- 返回成功时重点看:**`delete_executed`**、**`readback_status`**、**`safe_to_retry_delete`**、`summary.removed`。
|
|
211
|
-
- **`delete_executed=true` + `readback_status=deleted`**:删除已执行且回读确认不存在。
|
|
212
|
-
- **`delete_executed=true` + `readback_status=unavailable|still_exists`**:DELETE 已发出,但回读不可确认或短时间仍存在;**不要盲目重复删除**,稍后用 `builder portal get/list` 确认。
|
|
213
|
-
- DELETE 本身失败时才按 `status=failed` / `error_code` 处理。
|
|
214
|
-
|
|
215
|
-
---
|
|
216
|
-
|
|
217
|
-
## 8. **发布**(`--publish`)
|
|
218
|
-
|
|
219
|
-
- **默认会发布**:未显式 **`--no-publish`** 即 **`publish: true`**;公开入口仍是 **`portal_apply`**,发布动作由工具内部执行并回读 **`being_draft=false`**。
|
|
220
|
-
- **已发布线索**:成功路径上 **`published: true`**;**`live_result.publishStatus`** 常见 **`2`** 表示已发布(以你环境枚举为准)。
|
|
221
|
-
- **`partial_success` + `PORTAL_READBACK_PENDING`**:草稿/线上 **组件数量或元数据** 与「本次期望」不一致时会出现;**`PORTAL_*_VERIFICATION_INCOMPLETE`** 警告。应 **`builder portal get --dash-key …`**(草稿与 **`--no-being-draft` 线上**)**肉眼核对**区块是否齐全,而不是仅信 **`verified: true`**。
|
|
222
|
-
- **网关 503**:**与 CLI 无关**;环境恢复后重试 **`portal get` / `portal apply`**。(本轮在网关恢复后 **`portal apply` 已成功**。)
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
|
-
## 9. 本环境实测摘要
|
|
227
|
-
|
|
228
|
-
| 步骤 | 命令要点 | 结果 |
|
|
229
|
-
|------|-----------|------|
|
|
230
|
-
| 新建门户 | `--package-id 2030703`、`--dash-name "CLI门户探针_可删"`、`--no-publish`、单 **view** | **`dash_key`: `ecdcell64s02`** |
|
|
231
|
-
| 补报表 | **`builder charts apply`**,`name: 门户探针_目标图`,**`target`** | **`chart_id`: `mcp_fb104267c5c249ca`** |
|
|
232
|
-
| 503 后重试 | **`--dash-key ecdcell64s02`、`--publish`** + 含 **filter** 的六类 **`sections-file`** | **`published: true`**;**`filter` 仍不落库** → **`partial_success`**,**`components expected 6, got 5`**(与 **§3.4** 一致) |
|
|
233
|
-
| **filter 专项** | 仅 **chart + filter** 两块的多种 **`graphList`/`graphRef`/`dashChartId`/`filterGroupConfig`** 组合 | **回读恒为 1 块(仅 `type: 9`)** |
|
|
234
|
-
| **稳定可复现** | **`--publish`** + **[portal_sections_five_types.example.json](./portal_sections_five_types.example.json)**(**grid / text / link / view / chart**) | **`status: success`**,**`types: [2,4,5,9,10]`**,**`published: true`** |
|
|
235
|
-
| 删除门户 | `builder portal delete --dash-key etcivtmv5402` | **`status: success`**,**`delete_executed: true`**,**`readback_status: deleted`**,**`summary.removed: 1`** |
|
|
236
|
-
|
|
237
|
-
**推荐工作台模板示例**:[portal_sections_standard_workbench.example.json](./portal_sections_standard_workbench.example.json)
|
|
238
|
-
|
|
239
|
-
**含 `filter` 的六类示例(`filter` 是否落库视环境/后端而定)**:[portal_sections_all_types.example.json](./portal_sections_all_types.example.json)
|
|
240
|
-
|
|
241
|
-
---
|
|
242
|
-
|
|
243
|
-
## 10. 排障
|
|
244
|
-
|
|
245
|
-
| 现象 | 处理 |
|
|
246
|
-
|------|------|
|
|
247
|
-
| `portal apply accepts exactly one selector mode` | **`dash-key`** vs **`package-id`** 二选一。 |
|
|
248
|
-
| `PORTAL_READBACK_PENDING` / **`components expected N, got M`** | **`portal get`** 数组件;若多写了 **`filter`** 而回读少一块,见 **§3.4**(**CLI 写 filter 可能整体不生效**);也可能是 **异步回读**,可再 **`get`** 一次。 |
|
|
249
|
-
| **`published: true` 但 `verified: false`** | 读 **§7**;用 **`get` 草稿+线上**对照。 |
|
|
250
|
-
| **503** | 网关/运维;稍后重试。 |
|
|
251
|
-
| 缺 **chart** | **[QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md](./QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md)**:`builder charts apply`;或换有报表的应用。 |
|
|
252
|
-
| **VISIBILITY_CONFLICT** | 只用 **`--visibility-file`**。 |
|
|
253
|
-
| `delete_executed=true` 但 `readback_status=unavailable|still_exists` | DELETE 已发出,回读未确认;不要重复删除,稍后 `builder portal get/list` 复核。 |
|
|
254
|
-
|
|
255
|
-
---
|
|
256
|
-
|
|
257
|
-
## 11. 交叉引用
|
|
258
|
-
|
|
259
|
-
- [SKILL.md](../SKILL.md)
|
|
260
|
-
- [QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md](./QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md)
|
|
261
|
-
- [QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md](./QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md)
|
|
262
|
-
- [QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md](./QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md)
|
|
263
|
-
- [QINGFLOW_CLI_EXPLORATION_REPORT.md](./QINGFLOW_CLI_EXPLORATION_REPORT.md)
|
|
11
|
+
契约与九类组件专项 Schema:`qingflow --json builder portal schema --schema-kind complete|<类型>`。
|
|
@@ -47,7 +47,7 @@ Start from the portal job, then choose the section strategy. A portal is an asse
|
|
|
47
47
|
| Show BI visualizations | Use chart sections | `rows=7`; same-row components share the same `rows` |
|
|
48
48
|
| Show concrete data views | Use view sections | Use business views, not `全部数据` / `我的数据`; `rows=11` |
|
|
49
49
|
| Update one existing portal block | Prefer `patch_sections[]` | Do not replace the whole `sections` list unless intentional |
|
|
50
|
-
| Need a filter bar |
|
|
50
|
+
| Need a filter bar | Use `source_type: "filter"` with non-empty `config.rules` | Empty-rules filters are rejected (backend read face enumerates filter components by their rule groups); see §3.4 |
|
|
51
51
|
|
|
52
52
|
Short rule: **portal rows are 24-grid layout rows, not form-field rows. Same `y` = same row, same `rows`, `cols` sum 24.**
|
|
53
53
|
|
|
@@ -63,7 +63,7 @@ Before `portal apply`, verify this short list:
|
|
|
63
63
|
| Standard heights | Metric cards `rows=5`; BI charts `rows=7`; data views `rows=11`. Do not mix different heights in one row. |
|
|
64
64
|
| Standard order | Business entry -> metrics -> BI charts -> data views. Do not put data views before metrics/charts in a dashboard-style portal. |
|
|
65
65
|
| Default/system views | Do not use `全部数据` / `我的数据` as the main business portal views. Use concrete business views. |
|
|
66
|
-
| Filter component |
|
|
66
|
+
| Filter component | `config.rules` must contain at least one rule group (empty-rules filters are written but invisible in readback; blocked at validation). Rule keys: filterGroupName/filterGroupConfig/filterItemName/que/judge, see §3.4. |
|
|
67
67
|
| Readback | After apply, run `portal get` and compare section count, refs, and positions before claiming frontend visibility. |
|
|
68
68
|
|
|
69
69
|
Inline QingBI chart creation uses one chart job per section and runs with an internal concurrency limit of 10. If two sections declare the same `app_key + name + chart_type` with identical config, CLI creates/updates once and reuses the chart id. If their config differs, CLI rejects the portal write as a duplicate inline chart conflict.
|
|
@@ -90,7 +90,7 @@ Recommended standard workbench skeleton:
|
|
|
90
90
|
|
|
91
91
|
搭建侧稳定入口:**`qingflow builder portal`**:`list` / `get` / **`apply`** / **`delete`**(与根命令 **`qingflow portal`** 的「成员可读列表/详情」不同,见 **[QINGFLOW_CLI_EXPLORATION_REPORT.md](../core/QINGFLOW_CLI_EXPLORATION_REPORT.md) §4.6**)。
|
|
92
92
|
|
|
93
|
-
实现要点来自 CLI 打包内的 `builder_facade/models.py`(`PortalApplyRequest`、`PortalSectionPatch`)、`builder_facade/service.py`(`portal_apply` / `portal_delete`)、`cli/commands/builder.py`。已对 **`package_id=2030703`**、应用 **`ead8ims5i401`** **实跑**:**新建**(`--no-publish`)、**`--publish` 更新**(网关 **503** 恢复后已重试成功)、**五类区块可稳定落库**;**`filter
|
|
93
|
+
实现要点来自 CLI 打包内的 `builder_facade/models.py`(`PortalApplyRequest`、`PortalSectionPatch`)、`builder_facade/service.py`(`portal_apply` / `portal_delete`)、`cli/commands/builder.py`。已对 **`package_id=2030703`**、应用 **`ead8ims5i401`** **实跑**:**新建**(`--no-publish`)、**`--publish` 更新**(网关 **503** 恢复后已重试成功)、**五类区块可稳定落库**;**`filter` 声明式写入受控支持(rules 非空,历史「不落库」实测为空规则读面不可见,见 §3.4)**。另已对测试包 **`1414907`** 实跑 **`portal delete`**:临时门户 **`etcivtmv5402`** 删除后回读 **`readback_status=deleted`**。
|
|
94
94
|
|
|
95
95
|
> **权限**:**更新**需 **`edit_portal`**;**新建**按后端 `DashCtrl.createDash` 链路只预检目标包 **`add_app`**,不额外要求包 **`edit_app`**。失败时参见主技能与 **ADMIN** 速查。
|
|
96
96
|
|
|
@@ -141,7 +141,7 @@ qingflow --json builder portal get --dash-key "<DASH_KEY>" --no-being-draft
|
|
|
141
141
|
|
|
142
142
|
### 3.1 公共键
|
|
143
143
|
|
|
144
|
-
- **`title
|
|
144
|
+
- **`source_type`**(必填,小写)、**`title`**(可选——schema 仅强制 `source_type`;新建组件建议提供标题,存量 filter 组件合法无标题,round-trip 透传不要合成)、可选 **`position`**(嵌套 **`pc`/`mobile`** 各 **`x,y,cols,rows`**;平铺别名 **`pc_x/pc_y/pc_w/pc_h`、`mobile_*`** 也接受,`x/y` 与 `w/h/cols/rows` 混写均认;省略则由**实现自动排布**)。
|
|
145
145
|
- **`config`**、**`dash_style_config`**:按类型 Optional。默认只写 snake_case 规范键。
|
|
146
146
|
|
|
147
147
|
### 3.1.0 Payload 字段口径
|
|
@@ -214,7 +214,7 @@ mobile 固定按 **6 栅格** 从上到下堆叠:通常所有组件 `x=0, cols
|
|
|
214
214
|
|
|
215
215
|
**当前工具边界**:
|
|
216
216
|
|
|
217
|
-
- 声明式(`--file` / complete 档)支持**九类组件**:**`grid` / `chart` / `view` / `text` / `link`**(summary 档稳定面)+ **`carousel` / `task` / `favourite`**(complete 档,配置键见专项 Schema)+ **`filter`**(complete
|
|
217
|
+
- 声明式(`--file` / complete 档)支持**九类组件**:**`grid` / `chart` / `view` / `text` / `link`**(summary 档稳定面)+ **`carousel` / `task` / `favourite`**(complete 档,配置键见专项 Schema)+ **`filter`**(complete 档受控承载:`config.rules` 至少 1 组,空规则读面不可见、模型层阻断,见 **§3.4**)。
|
|
218
218
|
- **`task`(一个门户最多 1 个)与 `favourite`(一个门户最多 1 个)** 已进入 v1alpha1 complete 声明面(`task`/`favourite` 类型化 config 键),不要再按后端 raw 形状(`taskConfig`/`favouriteConfig`)手拼;声明形态见 `qingflow --json builder portal schema --schema-kind task|favourite`。旧文档中的「task/favorite 不在公开合约里」已过时。
|
|
219
219
|
- 推荐门户的可直接 apply 示例见 **[portal_sections_standard_workbench.example.json](../examples/portal/portal_sections_standard_workbench.example.json)**。这是唯一推荐复制的业务工作台布局模板:每一行 `cols_sum=24`,同行 `rows` 一致,下一行 `y` 连续。顶部先用一个业务入口 `grid` 占满 24 栅格;如果工具已支持待办组件,再把顶部拆成 `grid cols=12 + task cols=12`。
|
|
220
220
|
- **[portal_sections_five_types.example.json](../examples/portal/portal_sections_five_types.example.json)** 和 **[portal_sections_all_types.example.json](../examples/portal/portal_sections_all_types.example.json)** 仅用于组件能力探针,不作为业务门户布局模板;搭建正式门户时不要从这些探针推导布局。
|
|
@@ -253,7 +253,7 @@ mobile 固定按 **6 栅格** 从上到下堆叠:通常所有组件 `x=0, cols
|
|
|
253
253
|
| **`grid`** | **2** | 九宫格 | summary |
|
|
254
254
|
| **`text`** | **5** | 富文本说明 | summary |
|
|
255
255
|
| **`link`** | **4** | 外链 | summary |
|
|
256
|
-
| **`filter`** | **6** | 筛选条 | complete(
|
|
256
|
+
| **`filter`** | **6** | 筛选条 | complete(rules 必须非空) |
|
|
257
257
|
| **`carousel`** | **3** | 轮播(数据源应用 + 标题/图片/链接字段) | complete |
|
|
258
258
|
| **`task`** | **8** | 待办(一个门户最多 1 个) | complete |
|
|
259
259
|
| **`favourite`** | **7** | 收藏(一个门户最多 1 个) | complete |
|
|
@@ -292,14 +292,29 @@ CLI 校验(`PortalSectionPatch`):**chart** 须 **`chart`** 或 **`chart_re
|
|
|
292
292
|
}
|
|
293
293
|
```
|
|
294
294
|
|
|
295
|
-
### 3.4 **`filter
|
|
295
|
+
### 3.4 **`filter`**:声明式筛选条(支持,**rules 必须非空**)
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
声明形态:**`config.graphs`**(受筛选作用的图表引用)+ **`config.rules`**(筛选规则组,至少 1 组),编译为后端 `ComponentFilter`(`graphList` + `filterConfig`)。规则键集:`filterGroupName`(组名)/`filterGroupConfig`(筛选项:`filterItemName` + `que{queId}` + `judge{judgeType,judgeValues}`);代理键 `filterGroupId`/`filterItemId` 为后端自增位移键,声明里不要携带。
|
|
298
298
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"source_type": "filter",
|
|
302
|
+
"position": {"pc": {"x": 0, "y": 0, "cols": 24, "rows": 3}},
|
|
303
|
+
"config": {
|
|
304
|
+
"graphs": [{"chart_key": "CHART_KEY_1001"}],
|
|
305
|
+
"rules": [
|
|
306
|
+
{
|
|
307
|
+
"filterGroupName": "区域",
|
|
308
|
+
"filterGroupConfig": [
|
|
309
|
+
{"filterItemName": "城市", "que": {"queId": 123456}, "judge": {"judgeType": 3, "judgeValues": ["上海"]}}
|
|
310
|
+
]
|
|
311
|
+
}
|
|
312
|
+
]
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**关键边界(旧实测记录的真相)**:`rules` 为空的 filter **会被模型层直接拒绝**——后端读面按筛选组(dash_filter_group 行)反查筛选组件,空规则的组件行虽写入但读面枚举不到,表现为「apply 成功、回读缺 type:6」。历史上「filter 不落库」的实测记录(命令式 raw 直传时代、全部空规则组合)即此机制,不是后端拒绝 type:6。存量筛选组件合法无标题(提取产物不带 `title`,不要合成);round-trip 重放已验证(ws40013 基准 337 个 filter 组件一致率 100%)。专项 Schema:`qingflow --json builder portal schema --schema-kind filter`。
|
|
303
318
|
|
|
304
319
|
### 3.5 **`grid` / `text` / `link` / `view`**
|
|
305
320
|
|
|
@@ -348,7 +363,7 @@ qingflow --json builder portal delete --dash-key "<DASH_KEY>" > tmp/builder_port
|
|
|
348
363
|
|
|
349
364
|
## 8. **发布**(`--publish`)
|
|
350
365
|
|
|
351
|
-
- **默认会发布**:未显式 **`--no-publish`** 即 **`publish: true`**;公开入口仍是 **`portal_apply`**,发布动作由工具内部执行并回读 **`being_draft=false
|
|
366
|
+
- **默认会发布**:未显式 **`--no-publish`** 即 **`publish: true`**;公开入口仍是 **`portal_apply`**,发布动作由工具内部执行并回读 **`being_draft=false`**。**注意**:对**已发布门户**用 **`--no-publish`** 更新会把发布状态**降回草稿**(草稿写了、线上态被覆盖标记)——需要保留线上态的试写/迭代不要带 **`--no-publish`** 对已发布门户提交,或写完后显式补发布。
|
|
352
367
|
- **已发布线索**:成功路径上 **`published: true`**;**`live_result.publishStatus`** 常见 **`2`** 表示已发布(以你环境枚举为准)。
|
|
353
368
|
- **`partial_success` + `PORTAL_READBACK_PENDING`**:草稿/线上 **组件数量或元数据** 与「本次期望」不一致时会出现;**`PORTAL_*_VERIFICATION_INCOMPLETE`** 警告。应 **`builder portal get --dash-key …`**(草稿与 **`--no-being-draft` 线上**)**肉眼核对**区块是否齐全,而不是仅信 **`verified: true`**。
|
|
354
369
|
- **网关 503**:**与 CLI 无关**;环境恢复后重试 **`portal get` / `portal apply`**。(本轮在网关恢复后 **`portal apply` 已成功**。)
|
|
@@ -360,8 +375,8 @@ qingflow --json builder portal delete --dash-key "<DASH_KEY>" > tmp/builder_port
|
|
|
360
375
|
| 步骤 | 命令要点 | 结果 |
|
|
361
376
|
|------|-----------|------|
|
|
362
377
|
| 新建门户 | `--package-id 2030703`、`--dash-name "CLI门户探针_可删"`、`--no-publish`、单 **view** | **`dash_key`: `ecdcell64s02`** |
|
|
363
|
-
| 503 后重试 | **`--dash-key ecdcell64s02`、`--publish`** + 含 **filter** 的六类 **`sections-file`** | **`published: true
|
|
364
|
-
| **filter 专项** | 仅 **chart + filter** 两块的多种 **`graphList`/`graphRef`/`dashChartId`/`filterGroupConfig`**
|
|
378
|
+
| 503 后重试 | **`--dash-key ecdcell64s02`、`--publish`** + 含 **filter** 的六类 **`sections-file`** | **`published: true`**;当时 filter 组合全部**空 rules** → 读面不可见(**§3.4** 机制,非后端拒绝;现空 rules 已被模型层阻断) |
|
|
379
|
+
| **filter 专项** | 仅 **chart + filter** 两块的多种 **`graphList`/`graphRef`/`dashChartId`/`filterGroupConfig`** 组合(历史记录,全部空 rules) | **回读恒为 1 块(仅 `type: 9`)**——空规则读面不可见;含 rules 的声明式写入已由 ws40013 基准验证 |
|
|
365
380
|
| **稳定可复现** | **`--publish`** + **[portal_sections_five_types.example.json](../examples/portal/portal_sections_five_types.example.json)**(**grid / text / link / view / chart**) | **`status: success`**,**`types: [2,4,5,9,10]`**,**`published: true`** |
|
|
366
381
|
| 删除门户 | `builder portal delete --dash-key etcivtmv5402` | **`status: success`**,**`delete_executed: true`**,**`readback_status: deleted`**,**`summary.removed: 1`** |
|
|
367
382
|
|
|
@@ -376,7 +391,7 @@ qingflow --json builder portal delete --dash-key "<DASH_KEY>" > tmp/builder_port
|
|
|
376
391
|
| 现象 | 处理 |
|
|
377
392
|
|------|------|
|
|
378
393
|
| `portal apply accepts exactly one selector mode` | **`dash-key`** vs **`package-id`** 二选一。 |
|
|
379
|
-
| `PORTAL_READBACK_PENDING` / **`components expected N, got M`** | **`portal get`**
|
|
394
|
+
| `PORTAL_READBACK_PENDING` / **`components expected N, got M`** | **`portal get`** 数组件;`filter` 回读缺失多为 **空 rules**(模型层已阻断,检查报错)或 **异步回读**(可再 **`get`** 一次);含规则的 filter 写入正常(见 **§3.4**)。 |
|
|
380
395
|
| **`published: true` 但 `verified: false`** | 读 **§7**;用 **`get` 草稿+线上**对照。 |
|
|
381
396
|
| **503** | 网关/运维;稍后重试。 |
|
|
382
397
|
| 缺 **chart** | 在对应 portal section 写 **`chart`**;或换用已有报表的 **`chart: {"chart_id": "..."}`** / 兼容旧 **`chart_ref`**。 |
|
|
@@ -2513,8 +2513,11 @@ class PortalComponentPositionPatch(StrictModel):
|
|
|
2513
2513
|
# 因此显式 null 必须原样透传(round-trip 保真);仅键缺省时才回落布局默认值。
|
|
2514
2514
|
pc_x: int | None = Field(default=0, validation_alias=AliasChoices("pc_x", "pcX", "x"))
|
|
2515
2515
|
pc_y: int | None = Field(default=0, validation_alias=AliasChoices("pc_y", "pcY", "y"))
|
|
2516
|
-
|
|
2517
|
-
|
|
2516
|
+
# 宽高别名同时接受 w/h 与嵌套形态词汇 cols/rows:真实使用反馈显示 agent 常把
|
|
2517
|
+
# 嵌套词汇(cols/rows)与平铺词汇(x/y)混写(position: {"x":0,"y":0,"cols":24,"rows":5}),
|
|
2518
|
+
# 词汇表分裂是位写出错的高频形态(schema 描述同步说明)。
|
|
2519
|
+
pc_w: int | None = Field(default=12, validation_alias=AliasChoices("pc_w", "pcW", "w", "cols"))
|
|
2520
|
+
pc_h: int | None = Field(default=8, validation_alias=AliasChoices("pc_h", "pcH", "h", "rows"))
|
|
2518
2521
|
mobile_x: int | None = Field(default=0, validation_alias=AliasChoices("mobile_x", "mobileX"))
|
|
2519
2522
|
mobile_y: int | None = Field(default=0, validation_alias=AliasChoices("mobile_y", "mobileY"))
|
|
2520
2523
|
mobile_w: int | None = Field(default=6, validation_alias=AliasChoices("mobile_w", "mobileW"))
|
|
@@ -2721,6 +2724,18 @@ class PortalSectionPatch(StrictModel):
|
|
|
2721
2724
|
raise ValueError("task section requires task")
|
|
2722
2725
|
if self.source_type == "favourite" and self.favourite is None:
|
|
2723
2726
|
raise ValueError("favourite section requires favourite")
|
|
2727
|
+
if self.source_type == "filter":
|
|
2728
|
+
# 后端读面(DashSvcImpl#getComponentFilter)按筛选组(dash_filter_group 行)
|
|
2729
|
+
# 反查筛选组件:空规则的 filter 组件行虽会写入,但读面枚举不到——表现为
|
|
2730
|
+
# 「apply 成功但回读缺 type:6」(实测反馈「filter 不落库」的真身)。
|
|
2731
|
+
# 在模型咽喉(apply/validate 共同路径)阻断,不给静默不可见留门。
|
|
2732
|
+
filter_payload = self.config or {}
|
|
2733
|
+
rule_groups = filter_payload.get("rules", filter_payload.get("filterConfig"))
|
|
2734
|
+
if not isinstance(rule_groups, list) or not rule_groups:
|
|
2735
|
+
raise ValueError(
|
|
2736
|
+
"filter section requires config.rules with at least one rule group; "
|
|
2737
|
+
"an empty-rules filter is written but invisible in portal get readback"
|
|
2738
|
+
)
|
|
2724
2739
|
return self
|
|
2725
2740
|
|
|
2726
2741
|
|
|
@@ -2759,8 +2774,16 @@ class PortalApplyRequest(StrictModel):
|
|
|
2759
2774
|
payload = dict(value)
|
|
2760
2775
|
payload.pop("schema_version", None)
|
|
2761
2776
|
payload.pop("schemaVersion", None)
|
|
2762
|
-
|
|
2763
|
-
|
|
2777
|
+
# schema 文档声明的别名补全(dashKey/dashName):此前条件判断里出现了 dashName
|
|
2778
|
+
# 却从未消费,别名声明撞上 StrictModel 被拒,形成 validate 提示与 apply 行为
|
|
2779
|
+
# 不一致的观感(实测反馈失败二)。canonical 键优先;别名始终被消费(与 canonical
|
|
2780
|
+
# 同传时静默让位,不留残键触发 extra_forbidden)。
|
|
2781
|
+
if "dashKey" in payload:
|
|
2782
|
+
payload.setdefault("dash_key", payload.pop("dashKey"))
|
|
2783
|
+
if "name" in payload:
|
|
2784
|
+
payload.setdefault("dash_name", payload.pop("name"))
|
|
2785
|
+
if "dashName" in payload:
|
|
2786
|
+
payload.setdefault("dash_name", payload.pop("dashName"))
|
|
2764
2787
|
if "dash_global_config" in payload and "global_config" not in payload:
|
|
2765
2788
|
payload["global_config"] = payload.pop("dash_global_config")
|
|
2766
2789
|
elif "dashGlobalConfig" in payload and "global_config" not in payload:
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"type": "integer",
|
|
28
28
|
"minimum": 1,
|
|
29
29
|
"packageTagIdCompleteDescription": "归属应用包 tagId;创建必填,更新缺省沿用读面归属。别名 packageTagId/package_id/packageId。 创建(无 dash_key)时必填——由 portal_apply 实现层校验(PACKAGE_TAG_REQUIRED + 包新增权限守卫);更新(带 dash_key)缺省沿用读面归属,无所属包的门户提取产物合法缺省本字段。",
|
|
30
|
-
"x-backend": "dash.tags[0].tagId
|
|
30
|
+
"x-backend": "dash.tags[0].tagId"
|
|
31
31
|
},
|
|
32
32
|
"sections": {
|
|
33
33
|
"type": "array",
|
|
@@ -50,16 +50,16 @@
|
|
|
50
50
|
"publish": {
|
|
51
51
|
"type": "boolean",
|
|
52
52
|
"default": true,
|
|
53
|
-
"publishCompleteDescription": "apply
|
|
53
|
+
"publishCompleteDescription": "apply 后是否发布(true=写后发布并回读线上态;false=仅落草稿)。这是生命周期动作参数,不属于声明内容,幂等重放时用 false。注意:对已发布门户用 false 更新会把发布状态降回草稿——需要保留线上态时不要用 false 试写。"
|
|
54
54
|
},
|
|
55
55
|
"icon": {
|
|
56
56
|
"type": "string",
|
|
57
|
-
"portalIconCompleteDescription": "
|
|
57
|
+
"portalIconCompleteDescription": "门户图标名,必须取 workspace icon 目录(qingflow builder icon catalog,如 view-grid;不允许 template)。创建语义(无 dash_key)必须显式给出,省略会被拒绝;更新语义省略=保留存量图标。与 color 一起编码为 dashIcon。",
|
|
58
58
|
"x-backend": "dash.dashIcon(编码)"
|
|
59
59
|
},
|
|
60
60
|
"color": {
|
|
61
61
|
"type": "string",
|
|
62
|
-
"portalColorCompleteDescription": "
|
|
62
|
+
"portalColorCompleteDescription": "门户图标底色,创建语义必须取 workspace icon 目录色名(qing-orange/blue 等,目录见 qingflow builder icon catalog,hex 值如 #2468F2 会被拒绝);更新语义允许存量值(含旧版 hex)原样透传。与 icon 一起编码为 dashIcon。",
|
|
63
63
|
"x-backend": "dash.dashIcon(编码)"
|
|
64
64
|
},
|
|
65
65
|
"hide_copyright": {
|
|
@@ -403,7 +403,7 @@
|
|
|
403
403
|
"Position": {
|
|
404
404
|
"type": "object",
|
|
405
405
|
"additionalProperties": false,
|
|
406
|
-
"positionShapeCompleteDescription": "网格位置。PC 是 24 列网格(cols≤24),mobile 是 6 列网格(cols≤6);mobile 可省略。平铺别名 pc_x/pc_y/pc_w/pc_h、mobile_*
|
|
406
|
+
"positionShapeCompleteDescription": "网格位置。PC 是 24 列网格(cols≤24),mobile 是 6 列网格(cols≤6);mobile 可省略。平铺别名 pc_x/pc_y/pc_w/pc_h、mobile_* 也接受(x/y 与 w/h/cols/rows 混写均认)。",
|
|
407
407
|
"properties": {
|
|
408
408
|
"pc": {
|
|
409
409
|
"$ref": "#/$defs/PositionDetail"
|
|
@@ -944,7 +944,7 @@
|
|
|
944
944
|
"FilterConfig": {
|
|
945
945
|
"type": "object",
|
|
946
946
|
"additionalProperties": false,
|
|
947
|
-
"filterConfigShapeDescription": "筛选组件配置(
|
|
947
|
+
"filterConfigShapeDescription": "筛选组件配置(受控键集)。graphs 为受影响的图表引用;rules 为筛选规则组,键集 filterGroupName/filterGroupConfig/filterItemName/que/judge 系(受控透传,dataset 实测形态定稿;代理键 filterGroupId/filterItemId 剔除)。rules 必须至少 1 组——后端读面按筛选组反查筛选组件,空规则的 filter 写入后读面不可见(模型层已阻断)。",
|
|
948
948
|
"properties": {
|
|
949
949
|
"graphs": {
|
|
950
950
|
"type": "array",
|
|
@@ -965,7 +965,7 @@
|
|
|
965
965
|
},
|
|
966
966
|
"rules": {
|
|
967
967
|
"type": "array",
|
|
968
|
-
"filterRulesDescription": "筛选规则组(
|
|
968
|
+
"filterRulesDescription": "筛选规则组(至少 1 组;空规则组件读面不可见)。组键 filterGroupName(必填)/filterGroupConfig(筛选项:filterItemName/que{queId}/judge{judgeType,judgeValues})。",
|
|
969
969
|
"x-backend": "ComponentFilter.filterConfig(List<FilterConfig>)"
|
|
970
970
|
}
|
|
971
971
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"Position": {
|
|
8
8
|
"type": "object",
|
|
9
9
|
"additionalProperties": false,
|
|
10
|
-
"positionShapeDescription": "网格位置。PC 是 24 列网格(cols≤24),mobile 是 6 列网格(cols≤6);同行组件共享同一 y 与 rows,一行 cols 合计 24。mobile 可省略(缺省与 pc 同布局)。平铺别名 pc_x/pc_y/pc_w/pc_h、mobile_*
|
|
10
|
+
"positionShapeDescription": "网格位置。PC 是 24 列网格(cols≤24),mobile 是 6 列网格(cols≤6);同行组件共享同一 y 与 rows,一行 cols 合计 24。mobile 可省略(缺省与 pc 同布局)。平铺别名 pc_x/pc_y/pc_w/pc_h、mobile_* 也接受(x/y 与 w/h/cols/rows 混写均认),归一为本结构。标准行高:指标卡 rows=5、BI 图表 rows=7、数据视图 rows=11、业务入口 rows=4。",
|
|
11
11
|
"properties": {
|
|
12
12
|
"pc": {
|
|
13
13
|
"$ref": "#/$defs/PositionDetail"
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"Position": {
|
|
8
8
|
"type": "object",
|
|
9
9
|
"additionalProperties": false,
|
|
10
|
-
"positionShapeDescription": "网格位置。PC 是 24 列网格(cols≤24),mobile 是 6 列网格(cols≤6);同行组件共享同一 y 与 rows,一行 cols 合计 24。mobile 可省略(缺省与 pc 同布局)。平铺别名 pc_x/pc_y/pc_w/pc_h、mobile_*
|
|
10
|
+
"positionShapeDescription": "网格位置。PC 是 24 列网格(cols≤24),mobile 是 6 列网格(cols≤6);同行组件共享同一 y 与 rows,一行 cols 合计 24。mobile 可省略(缺省与 pc 同布局)。平铺别名 pc_x/pc_y/pc_w/pc_h、mobile_* 也接受(x/y 与 w/h/cols/rows 混写均认),归一为本结构。标准行高:指标卡 rows=5、BI 图表 rows=7、数据视图 rows=11、业务入口 rows=4。",
|
|
11
11
|
"properties": {
|
|
12
12
|
"pc": {
|
|
13
13
|
"$ref": "#/$defs/PositionDetail"
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"Position": {
|
|
8
8
|
"type": "object",
|
|
9
9
|
"additionalProperties": false,
|
|
10
|
-
"positionShapeDescription": "网格位置。PC 是 24 列网格(cols≤24),mobile 是 6 列网格(cols≤6);同行组件共享同一 y 与 rows,一行 cols 合计 24。mobile 可省略(缺省与 pc 同布局)。平铺别名 pc_x/pc_y/pc_w/pc_h、mobile_*
|
|
10
|
+
"positionShapeDescription": "网格位置。PC 是 24 列网格(cols≤24),mobile 是 6 列网格(cols≤6);同行组件共享同一 y 与 rows,一行 cols 合计 24。mobile 可省略(缺省与 pc 同布局)。平铺别名 pc_x/pc_y/pc_w/pc_h、mobile_* 也接受(x/y 与 w/h/cols/rows 混写均认),归一为本结构。标准行高:指标卡 rows=5、BI 图表 rows=7、数据视图 rows=11、业务入口 rows=4。",
|
|
11
11
|
"properties": {
|
|
12
12
|
"pc": {
|
|
13
13
|
"$ref": "#/$defs/PositionDetail"
|