@qingflow-tech/qingflow-cli 1.1.61-beta.3 → 1.1.61-beta.5
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 +1 -1
- package/skills/qingflow-cli/SKILL.md +2 -0
- package/skills/qingflow-cli/reference/builder/30-schema-fields.md +13 -0
- package/skills/qingflow-cli/reference/builder/70-portal.md +37 -16
- package/skills/qingflow-cli/reference/data/README.md +38 -0
- package/skills/qingflow-cli/reference/examples/portal/portal_apply_v1alpha1.example.json +100 -0
- package/skills/qingflow-cli/reference/examples/portal/portal_sections_all_types.example.json +6 -7
- package/src/qingflow_mcp/app_form_validator.py +20 -3
- package/src/qingflow_mcp/builder_facade/app_form.py +137 -17
- package/src/qingflow_mcp/builder_facade/models.py +164 -13
- package/src/qingflow_mcp/builder_facade/portals_schema.py +397 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/manifest.json +14 -1
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/complete.json +49 -9
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +27 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_content_classification.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_content_extraction.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_content_generation.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_document_parsing.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_image_generation.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_image_recognition.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_machine_translation.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ai_text_summary.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +2 -2
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +27 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date_duration.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/embed_field.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/image_select.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/location.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +2 -2
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/ocr.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +2 -2
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +27 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +29 -2
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +20 -7
- package/src/qingflow_mcp/builder_facade/schemas/portals/manifest.json +59 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/complete.json +1397 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/carousel.json +193 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/chart.json +643 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/favourite.json +203 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/filter.json +268 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/grid.json +265 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/link.json +131 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/task.json +199 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/text.json +123 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/components/view.json +196 -0
- package/src/qingflow_mcp/builder_facade/schemas/portals/v1alpha1/summary.json +800 -0
- package/src/qingflow_mcp/builder_facade/service.py +1104 -71
- package/src/qingflow_mcp/cli/commands/builder.py +114 -5
- package/src/qingflow_mcp/public_surface.py +2 -0
- package/src/qingflow_mcp/server_app_builder.py +32 -1
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +73 -0
- package/src/qingflow_mcp/solution/executor.py +5 -2
- package/src/qingflow_mcp/tools/ai_builder_tools.py +110 -12
- package/src/qingflow_mcp/tools/portal_tools.py +4 -54
- package/src/qingflow_mcp/tools/portal_writer.py +59 -0
- package/src/qingflow_mcp/tools/solution_tools.py +2 -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.5
|
|
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.5 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.5 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.5",
|
|
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
|
@@ -14,6 +14,8 @@ description: |
|
|
|
14
14
|
|
|
15
15
|
本 Skill 负责 CLI 入口、文件约定和业务分流。已知 `app_key` 时直接执行 `data query/get`。只有应用名称时,先用 `qingflow --json app list --query "应用名称"` 查找当前工作区的可见应用,并核对返回项的 `app_name/title`;目标明确且唯一时取其 `app_key` 再查询,否则请用户确认。不要把应用名称直接传给 `--app-key`,也不要为定位名称转向 builder 或切换工作区。字段能力、权限范围与下一步动作以当前工具 schema、data 响应和 `hints` 为准。
|
|
16
16
|
|
|
17
|
+
首次解析 `data query/get` 响应前,阅读 [data 响应结构与解析](./reference/data/README.md#响应结构与解析),按同次响应中的 schema 与记录结构读取目标字段。
|
|
18
|
+
|
|
17
19
|
## 关联文件
|
|
18
20
|
|
|
19
21
|
|
|
@@ -218,6 +218,19 @@ current CLI payload.
|
|
|
218
218
|
| `relation` | 关联记录(**`config.relationMode`**:`single` \| `multiple`) |
|
|
219
219
|
| `data_relation` | 数据关联/引用填充 |
|
|
220
220
|
| `subtable` | 子表(**`subfields[]`**) |
|
|
221
|
+
| `date_duration` | 日期区间(透传型,无特殊配置) |
|
|
222
|
+
| `image_select` | 图片单选(透传型,选项经模板保真) |
|
|
223
|
+
| `location` | 地理位置(透传型) |
|
|
224
|
+
| `ocr` | OCR 识别(透传型) |
|
|
225
|
+
| `embed_field` | 内嵌字段(透传型) |
|
|
226
|
+
| `ai_content_generation` | AI 内容生成(透传型) |
|
|
227
|
+
| `ai_content_extraction` | AI 内容提取(透传型) |
|
|
228
|
+
| `ai_content_classification` | AI 内容分类(透传型) |
|
|
229
|
+
| `ai_machine_translation` | AI 机器翻译(透传型) |
|
|
230
|
+
| `ai_image_recognition` | AI 图像识别(透传型) |
|
|
231
|
+
| `ai_image_generation` | AI 图像生成(透传型) |
|
|
232
|
+
| `ai_document_parsing` | AI 文档解析(透传型) |
|
|
233
|
+
| `ai_text_summary` | AI 文本摘要(透传型) |
|
|
221
234
|
|
|
222
235
|
历史表单中的后端 `queType=1` 是“描述文字”布局控件,不属于上述新建字段类型。`app-form get` 为了保持完整声明可校验,会将它兼容输出为 `long_text`;`app-form apply` 会根据已有字段模板恢复原始 `queType=1` 和 `queDefaultValue`,所以修改其他字段时不会丢失描述文字。新建可填写多行文本仍使用 `long_text`,不要用它创建描述文字控件。
|
|
223
236
|
|
|
@@ -11,13 +11,26 @@ Not responsible for: creating apps or views referenced by the portal. Create or
|
|
|
11
11
|
## Main chain
|
|
12
12
|
|
|
13
13
|
```text
|
|
14
|
-
portal list/get if updating -> verify apps/views and read chart lists only when reusing existing charts
|
|
14
|
+
portal list/get if updating -> verify apps/views and read chart lists only when reusing existing charts
|
|
15
|
+
-> (offline) builder portal validate --file decl.json
|
|
16
|
+
-> builder portal apply --file decl.json [--schema-version v1alpha1]
|
|
17
|
+
-> builder portal get --dash-key K --schema-kind summary|complete (readback) -> publish state
|
|
15
18
|
```
|
|
16
19
|
|
|
20
|
+
## Versioned schema and declaration files
|
|
21
|
+
|
|
22
|
+
- Declarations are versioned: `schema_version` (default `v1alpha1`), `apiVersion: builder.qingflow.com/v1alpha1`. The envelope echoes `schemaVersion` when you pass `--schema-version`.
|
|
23
|
+
- Two document kinds: **summary** = name + layout + component list (five stable component types: `chart/view/grid/text/link`); **complete** = summary superset with global config (`icon/color/hide_copyright/global_config/style_template_id/overall_style`), nine component types (adds `filter/carousel/task/favourite`), component `style_config`, and `visibility` authorization.
|
|
24
|
+
- Read the contract itself: `qingflow --json builder portal schema --schema-kind summary|complete|<component>`; every component type (`chart|view|grid|filter|text|link|carousel|task|favourite`) has a self-contained schema — fetch it before guessing config keys.
|
|
25
|
+
- `builder portal get` defaults to `--schema-kind summary` and returns a canonical `declaration` (the round-trip anchor for replay tooling). Use `--schema-kind raw` for the legacy normalized response and `--schema-kind complete` for the full declaration.
|
|
26
|
+
- Offline validation: `qingflow --json builder portal validate --file decl.json` (accepts `-` for stdin; zero backend calls). Fix issues before applying.
|
|
27
|
+
- Apply main form: `qingflow builder portal apply --file decl.json --schema-version v1alpha1 [--no-publish]`. Legacy patch parameters (`--sections-file`, `--visibility-file`, ...) still work in a compatibility window but emit a deprecation notice; do not use them in new automations. `--payload-file` (raw DTO passthrough) is legacy-only.
|
|
28
|
+
|
|
17
29
|
## Demo files
|
|
18
30
|
|
|
19
31
|
| Scenario | Example |
|
|
20
32
|
|----------|---------|
|
|
33
|
+
| Recommended v1alpha1 declaration (`--file`) | [portal_apply_v1alpha1.example.json](../examples/portal/portal_apply_v1alpha1.example.json) |
|
|
21
34
|
| Recommended business workbench | [portal_sections_standard_workbench.example.json](../examples/portal/portal_sections_standard_workbench.example.json) |
|
|
22
35
|
| Stable component capability probe | [portal_sections_five_types.example.json](../examples/portal/portal_sections_five_types.example.json) |
|
|
23
36
|
| Raw/experimental all-types probe | [portal_sections_all_types.example.json](../examples/portal/portal_sections_all_types.example.json) |
|
|
@@ -66,8 +79,9 @@ Recommended standard workbench skeleton:
|
|
|
66
79
|
|
|
67
80
|
## Update existing portal
|
|
68
81
|
|
|
69
|
-
-
|
|
70
|
-
-
|
|
82
|
+
- Prefer the declaration-file form: `builder portal apply --file decl.json --schema-version v1alpha1 --dash-key K --no-publish` (the file may omit `dash_key` and pass it via `--dash-key`; do not send `package_id` for updates).
|
|
83
|
+
- Read the current declaration first with `builder portal get --dash-key K --schema-kind complete`, edit it, `builder portal validate --file`, then apply. `sections` is full-replace semantics.
|
|
84
|
+
- `patch_sections[]` (via `--patch-sections-file`, compatibility window) remains for targeted section changes when a full declaration round-trip is not practical.
|
|
71
85
|
- `patch_sections[]` selectors may use `order`, `chart_ref.chart_id`, `chart_ref.chart_key`, or `view_ref.view_key`; chart sections read back from portal often only include `chart_id`, so patching by `chart_id` is valid and does not require `app_key`.
|
|
72
86
|
- If using a business-entry grid, include real `items`; do not submit an empty entry container.
|
|
73
87
|
- Validate referenced `chart_key`, `chart_id`, `view_key`, or `app_key` before claiming the portal is complete.
|
|
@@ -110,7 +124,9 @@ qingflow --json builder portal get --dash-key "<DASH_KEY>" --no-being-draft
|
|
|
110
124
|
| 参数 | 说明 |
|
|
111
125
|
|------|------|
|
|
112
126
|
| **`--publish` / `--no-publish`** | 默认 **`--publish`**。仅 **`--no-publish`** 时不会调用发布接口;契约说明 **`publish=false` 不宣称线上已变**。 |
|
|
113
|
-
| **`--
|
|
127
|
+
| **`--file`**(主形态) | 完整门户声明 JSON 对象(支持 **`-`** stdin);与 patch 参数互斥;声明内可带 **`schema_version`**。 |
|
|
128
|
+
| **`--schema-version`** | 固定声明契约版本(缺省读文件内 `schema_version`,再缺省 latest)。传入时声明先过 complete schema 离线校验,**`INVALID_SPEC` 不写**。 |
|
|
129
|
+
| **`--payload-file`**(旧) | 完整门户 JSON 对象透传(兼容窗口);新代码改用 **`--file`**。标准字段写 `dash_name`。 |
|
|
114
130
|
| **`--sections-file`** | JSON **数组**;**全量替换**。省略时仅改基础信息,且 **`hide_copyright` / `dash_global_config` / `config`** 不能单独提交(**`PORTAL_SECTIONS_REQUIRED`**)。 |
|
|
115
131
|
| **`--layout-preset`** | 可选:**`auto` / `dashboard_2col` / `dashboard_3col`**。当区块未显式写 **`position`** 时由工具生成大屏布局。 |
|
|
116
132
|
| **`--visibility-file`** | 与 **`--auth-file`** 不能同时用。 |
|
|
@@ -198,8 +214,8 @@ mobile 固定按 **6 栅格** 从上到下堆叠:通常所有组件 `x=0, cols
|
|
|
198
214
|
|
|
199
215
|
**当前工具边界**:
|
|
200
216
|
|
|
201
|
-
-
|
|
202
|
-
-
|
|
217
|
+
- 声明式(`--file` / complete 档)支持**九类组件**:**`grid` / `chart` / `view` / `text` / `link`**(summary 档稳定面)+ **`carousel` / `task` / `favourite`**(complete 档,配置键见专项 Schema)+ **`filter`**(complete 档受控承载,仍按 **§3.4** raw 边界处理,不作为主路径)。
|
|
218
|
+
- **`task`(一个门户最多 1 个)与 `favourite`(一个门户最多 1 个)** 已进入 v1alpha1 complete 声明面(`task`/`favourite` 类型化 config 键),不要再按后端 raw 形状(`taskConfig`/`favouriteConfig`)手拼;声明形态见 `qingflow --json builder portal schema --schema-kind task|favourite`。旧文档中的「task/favorite 不在公开合约里」已过时。
|
|
203
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`。
|
|
204
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)** 仅用于组件能力探针,不作为业务门户布局模板;搭建正式门户时不要从这些探针推导布局。
|
|
205
221
|
- `grid` 必须写 **`config.items[]`**;只写 `gridTitle` / `beingShowTitle` 会生成空入口容器,工具会返回 **`PORTAL_GRID_ITEMS_EMPTY`**。
|
|
@@ -230,16 +246,21 @@ mobile 固定按 **6 栅格** 从上到下堆叠:通常所有组件 `x=0, cols
|
|
|
230
246
|
|
|
231
247
|
### 3.2 `source_type` ↔ 回读 `components[].type`(搭建侧)
|
|
232
248
|
|
|
233
|
-
| `source_type` | 回读 `type`(数字) | 搭建侧含义 |
|
|
234
|
-
|
|
235
|
-
| **`chart`** | **9** | QingBI 图表块 |
|
|
236
|
-
| **`view`** | **10** | 应用视图块 |
|
|
237
|
-
| **`grid`** | **2** | 九宫格 |
|
|
238
|
-
| **`
|
|
239
|
-
| **`
|
|
240
|
-
| **`
|
|
241
|
-
|
|
242
|
-
|
|
249
|
+
| `source_type` | 回读 `type`(数字) | 搭建侧含义 | 声明档 |
|
|
250
|
+
|---------------|---------------------|------------|----------|
|
|
251
|
+
| **`chart`** | **9** | QingBI 图表块 | summary |
|
|
252
|
+
| **`view`** | **10** | 应用视图块 | summary |
|
|
253
|
+
| **`grid`** | **2** | 九宫格 | summary |
|
|
254
|
+
| **`text`** | **5** | 富文本说明 | summary |
|
|
255
|
+
| **`link`** | **4** | 外链 | summary |
|
|
256
|
+
| **`filter`** | **6** | 筛选条 | complete(raw/unstable) |
|
|
257
|
+
| **`carousel`** | **3** | 轮播(数据源应用 + 标题/图片/链接字段) | complete |
|
|
258
|
+
| **`task`** | **8** | 待办(一个门户最多 1 个) | complete |
|
|
259
|
+
| **`favourite`** | **7** | 收藏(一个门户最多 1 个) | complete |
|
|
260
|
+
|
|
261
|
+
每类组件的详细配置键经专项 Schema 获取:`qingflow --json builder portal schema --schema-kind <类型>`(如 `--schema-kind carousel`、`--schema-kind task`)。`carousel` 的 `title_field/image_field/link_field` 填字段显示名(CLI 编译为 queId);`task.tasks[].task_type` 取值 1=我的待办 2=已超时 3=即将超时 4=催办 5=我发起的 6=抄送我的 7=草稿箱 8=我的已办 9=待完善;`favourite.items[]` 用 `item_type`(1=应用 2=门户)+`link_key` 锚定目标。
|
|
262
|
+
|
|
263
|
+
CLI 校验(`PortalSectionPatch`):**chart** 须 **`chart`** 或 **`chart_ref`**;**view** 须 **`view_ref`**;**text** 须 **`text`**;**link** 须 **`url`**;**carousel/task/favourite** 须各自类型化 config 键。**grid** / **filter** 无额外 ref,**`config` 形状由专项 Schema 与后端接受为准**。
|
|
243
264
|
|
|
244
265
|
### 3.3 **`chart`**:一个字段同时支持引用已有图表和内联创建图表
|
|
245
266
|
|
|
@@ -10,6 +10,44 @@
|
|
|
10
10
|
|
|
11
11
|
参数、JSON 文件形状和可选项必须来自当前工具 schema、当次 `--help` 或响应字段能力,不得从其他命令推测。
|
|
12
12
|
|
|
13
|
+
## 响应结构与解析
|
|
14
|
+
|
|
15
|
+
`data query` 的记录位于 `records.items[]`,`data get` 的单条记录位于 `record`。两者的业务值都在各记录的 `fields` 对象中,以十进制 `field_id` 字符串为键;不要从记录顶层按字段 id 取值。
|
|
16
|
+
|
|
17
|
+
`schema.fields[]` 是字段元数据,包含两种结构:系统字段使用 `key` 与 `kind=system`,对应记录顶层的 `record_id/creator/updated_at`;业务字段使用 `field_id/title/data_type/cardinality`,对应记录的 `fields`。按是否包含 `field_id` 识别业务字段(`field_id=0` 也有效),类型读取 `data_type`。子表的列定义在 `children` 中,各行同样以字段 id 字符串为键。
|
|
18
|
+
|
|
19
|
+
以下是只保留解析所需字段的 query 响应示例:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"schema": {"fields": [
|
|
24
|
+
{"key": "record_id", "kind": "system"},
|
|
25
|
+
{"field_id": 0, "title": "编号", "data_type": "number", "cardinality": "one"},
|
|
26
|
+
{"field_id": 101, "title": "名称", "data_type": "text", "cardinality": "one"}
|
|
27
|
+
]},
|
|
28
|
+
"records": {"items": [
|
|
29
|
+
{"record_id": "1", "fields": {"0": 0, "101": "示例"}}
|
|
30
|
+
]}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
JSON 落盘后,先确认记录结构,再按用户目标选择字段;例如只查看首条记录的「名称」:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
field = next(f for f in payload["schema"]["fields"]
|
|
38
|
+
if "field_id" in f and f["title"] == "名称")
|
|
39
|
+
key = str(field["field_id"])
|
|
40
|
+
for record in payload["records"]["items"][:1]:
|
|
41
|
+
values = record["fields"]
|
|
42
|
+
assert isinstance(values, dict), "记录 fields 结构不符合预期"
|
|
43
|
+
if key in values:
|
|
44
|
+
print({"record_id": record["record_id"], "title": field["title"], "value": values[key]})
|
|
45
|
+
else:
|
|
46
|
+
print({"record_id": record["record_id"], "title": field["title"], "returned": False})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
键未返回与值为 `null` 是不同情况;`0` 和 `false` 是有效值,不要用真假判断把它们归为空。结构不符合预期或字段键缺失时,检查同次响应的 schema、`access`、`hints` 与首条记录的结构,必要时对同一记录执行 `data get` 复核;不能把解析失败或未返回字段直接报告为业务数据为空。
|
|
50
|
+
|
|
13
51
|
## 字段能力合同
|
|
14
52
|
|
|
15
53
|
1. 首次操作当前应用时,只通过只读 `data query/get` 请求 `include_schema=true`,读取同一次响应中的 `schema` 与递归 `field_capabilities`;不得用 insert/update 探测能力。
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "v1alpha1",
|
|
3
|
+
"name": "业务工作台",
|
|
4
|
+
"package_tag_id": 2030703,
|
|
5
|
+
"icon": "view-grid",
|
|
6
|
+
"color": "#2468F2",
|
|
7
|
+
"hide_copyright": false,
|
|
8
|
+
"global_config": {
|
|
9
|
+
"auto_refresh": false,
|
|
10
|
+
"interval": 60,
|
|
11
|
+
"export": true
|
|
12
|
+
},
|
|
13
|
+
"visibility": {
|
|
14
|
+
"mode": "workspace",
|
|
15
|
+
"external_mode": "not"
|
|
16
|
+
},
|
|
17
|
+
"layout_preset": "auto",
|
|
18
|
+
"sections": [
|
|
19
|
+
{
|
|
20
|
+
"title": "业务入口",
|
|
21
|
+
"source_type": "grid",
|
|
22
|
+
"position": {
|
|
23
|
+
"pc": { "x": 0, "y": 0, "cols": 24, "rows": 4 }
|
|
24
|
+
},
|
|
25
|
+
"config": {
|
|
26
|
+
"gridTitle": "业务入口",
|
|
27
|
+
"beingShowTitle": true,
|
|
28
|
+
"items": [
|
|
29
|
+
{ "type": 1, "jumpMode": 1, "linkAppKey": "APP_KEY_DEVICE", "linkFormType": 1, "title": "设备台账" },
|
|
30
|
+
{ "type": 1, "jumpMode": 1, "linkAppKey": "APP_KEY_WORKORDER", "linkFormType": 1, "title": "工单管理" }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"title": "设备总数",
|
|
36
|
+
"source_type": "chart",
|
|
37
|
+
"role": "metric",
|
|
38
|
+
"position": {
|
|
39
|
+
"pc": { "x": 0, "y": 4, "cols": 6, "rows": 5 }
|
|
40
|
+
},
|
|
41
|
+
"chart": {
|
|
42
|
+
"app_key": "APP_KEY_DEVICE",
|
|
43
|
+
"name": "设备总数",
|
|
44
|
+
"chart_type": "target",
|
|
45
|
+
"metric": "count(*)"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"title": "异常设备数",
|
|
50
|
+
"source_type": "chart",
|
|
51
|
+
"role": "metric",
|
|
52
|
+
"position": {
|
|
53
|
+
"pc": { "x": 6, "y": 4, "cols": 6, "rows": 5 }
|
|
54
|
+
},
|
|
55
|
+
"chart": {
|
|
56
|
+
"app_key": "APP_KEY_DEVICE",
|
|
57
|
+
"name": "异常设备数",
|
|
58
|
+
"chart_type": "target",
|
|
59
|
+
"metric": "count(*)",
|
|
60
|
+
"filters": [
|
|
61
|
+
{ "field_name": "状态", "operator": "eq", "values": ["异常"] }
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"title": "工单趋势",
|
|
67
|
+
"source_type": "chart",
|
|
68
|
+
"position": {
|
|
69
|
+
"pc": { "x": 0, "y": 9, "cols": 12, "rows": 7 }
|
|
70
|
+
},
|
|
71
|
+
"chart": {
|
|
72
|
+
"app_key": "APP_KEY_WORKORDER",
|
|
73
|
+
"name": "工单趋势",
|
|
74
|
+
"chart_type": "line",
|
|
75
|
+
"metric": "count(*)",
|
|
76
|
+
"group_by": ["创建日期"]
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"title": "工单类型分布",
|
|
81
|
+
"source_type": "chart",
|
|
82
|
+
"position": {
|
|
83
|
+
"pc": { "x": 12, "y": 9, "cols": 12, "rows": 7 }
|
|
84
|
+
},
|
|
85
|
+
"chart_ref": { "app_key": "APP_KEY_WORKORDER", "chart_name": "工单类型分布" }
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"title": "异常设备明细",
|
|
89
|
+
"source_type": "view",
|
|
90
|
+
"position": {
|
|
91
|
+
"pc": { "x": 0, "y": 16, "cols": 24, "rows": 11 }
|
|
92
|
+
},
|
|
93
|
+
"view_ref": {
|
|
94
|
+
"app_key": "APP_KEY_DEVICE",
|
|
95
|
+
"view_key": "VIEW_KEY_ABNORMAL_DEVICES"
|
|
96
|
+
},
|
|
97
|
+
"view_display": { "show_title": true }
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
package/skills/qingflow-cli/reference/examples/portal/portal_sections_all_types.example.json
CHANGED
|
@@ -17,14 +17,13 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"config": {
|
|
20
|
-
"
|
|
21
|
-
"graphList": [
|
|
20
|
+
"graphs": [
|
|
22
21
|
{
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"graphName": "可选_图表显示名"
|
|
22
|
+
"chart_key": "REPLACE_WITH_QINGBI_CHART_ID",
|
|
23
|
+
"chart_name": "可选_图表显示名"
|
|
26
24
|
}
|
|
27
|
-
]
|
|
25
|
+
],
|
|
26
|
+
"rules": []
|
|
28
27
|
}
|
|
29
28
|
},
|
|
30
29
|
{
|
|
@@ -142,4 +141,4 @@
|
|
|
142
141
|
"chart_id": "YOUR_CHART_ID"
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
|
-
]
|
|
144
|
+
]
|
|
@@ -28,10 +28,16 @@ _INTEGRATION_OUTPUT_TARGET_FIELD_TYPES = {
|
|
|
28
28
|
"single_select",
|
|
29
29
|
"multi_select",
|
|
30
30
|
"attachment",
|
|
31
|
+
# r10 存量实证放行:link(92nl6blfk402)/member(ab9fjvj33001)——后端写链
|
|
32
|
+
# 无类型白名单,运行时同为字符串写入路径
|
|
33
|
+
"link",
|
|
34
|
+
"member",
|
|
31
35
|
"data_relation",
|
|
32
36
|
"subtable",
|
|
33
37
|
}
|
|
34
|
-
|
|
38
|
+
# key 允许含空白(r10 实测存量 key 如「应用包 ID」带空格,\s 排除导致占位符
|
|
39
|
+
# 扫描漏检 → validate 误报「key must appear as {{key}}」)
|
|
40
|
+
_Q_LINKER_TEMPLATE_RE = re.compile(r"\{\{(?P<key>[^{}]+)\}\}")
|
|
35
41
|
_FORMULA_IDENTIFIER_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*")
|
|
36
42
|
#: 公式字段引用正则(#queId_<id>$,支持表格嵌套 <id>.<id>)
|
|
37
43
|
_FORMULA_FIELD_REF_RE = re.compile(r"#queId_([^$]+)\$")
|
|
@@ -353,7 +359,15 @@ def _static_errors(payload: dict[str, Any]) -> list[tuple[str, str]]:
|
|
|
353
359
|
"is_empty and is_not_empty require a literal operand with values: []",
|
|
354
360
|
)
|
|
355
361
|
)
|
|
356
|
-
elif
|
|
362
|
+
elif (
|
|
363
|
+
operand.get("kind") == "literal"
|
|
364
|
+
and not values
|
|
365
|
+
and operator not in {"subordinate_to", "not_subordinate_to"}
|
|
366
|
+
):
|
|
367
|
+
# subordinate 两算子放行空 literal:存量「设置后清空」形态
|
|
368
|
+
# (r13 实证 41ce0ad8 六处)——后端写链 checkRelationMatchRules
|
|
369
|
+
# 仅校验 dynamic_range(FormSvcImpl:10448),formula 渲染对空
|
|
370
|
+
# judgeValueDetails 输出 [](Formula:397),读回解析对称,往返保真
|
|
357
371
|
errors.append(
|
|
358
372
|
(
|
|
359
373
|
f"{operand_path}/values",
|
|
@@ -386,7 +400,10 @@ def _static_errors(payload: dict[str, Any]) -> list[tuple[str, str]]:
|
|
|
386
400
|
selected_name = selected[1].get("name") if selected is not None else selector.get("name")
|
|
387
401
|
if not selected_name and selector.get("queId") in fields_by_id:
|
|
388
402
|
selected_name = fields_by_id[selector["queId"]].get("name")
|
|
389
|
-
|
|
403
|
+
# 两侧 strip:模板引用名提取时已 strip(字段名存量带尾随空格,如
|
|
404
|
+
# am4oh08v2401「答:Answer 」),严格相等会把「引用名≠当前字段名」的
|
|
405
|
+
# 存量形态误报为 key 不一致
|
|
406
|
+
if selected_name and str(item.get("key") or "").strip() != str(selected_name).strip():
|
|
390
407
|
errors.append((f"{input_path}/key", "json_path key must equal the selected field name"))
|
|
391
408
|
|
|
392
409
|
if field_type == "q_linker":
|