@qingflow-tech/qingflow-cli 1.1.50-beta.2 → 1.1.50-beta.4
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 +7 -2
- package/skills/qingflow-cli/SKILL.md +74 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +3 -9
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +1 -0
- package/skills/qingflow-cli/reference/builder/50-views.md +180 -191
- package/skills/qingflow-cli/reference/builder/README.md +1 -1
- package/skills/qingflow-cli/reference/builder/reference/app-delivery-sop.md +3 -3
- package/skills/qingflow-cli/reference/examples/README.md +1 -1
- package/skills/qingflow-cli/reference/examples/views/views_apply_v1alpha1.example.json +14 -0
- package/skills/qingflow-cli/reference/examples/views/views_upsert_table_minimal.example.json +30 -36
- package/src/qingflow_mcp/builder_facade/models.py +227 -17
- package/src/qingflow_mcp/builder_facade/schemas/views/manifest.json +42 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/complete.json +1245 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/button-config.json +53 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/data-permission.json +60 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/display.json +73 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/operation-permission.json +48 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/query-condition.json +47 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/summary.json +319 -0
- package/src/qingflow_mcp/builder_facade/service.py +1460 -410
- package/src/qingflow_mcp/builder_facade/views_extract.py +592 -0
- package/src/qingflow_mcp/builder_facade/views_schema.py +400 -0
- package/src/qingflow_mcp/cli/commands/builder.py +72 -53
- package/src/qingflow_mcp/public_surface.py +3 -0
- package/src/qingflow_mcp/response_trim.py +11 -2
- package/src/qingflow_mcp/server_app_builder.py +32 -8
- package/src/qingflow_mcp/solution/compiler/view_compiler.py +2 -1
- package/src/qingflow_mcp/solution/executor.py +96 -8
- package/src/qingflow_mcp/solution/spec_models.py +1 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +211 -393
- package/src/qingflow_mcp/tools/solution_tools.py +2 -0
- package/src/qingflow_mcp/tools/view_tools.py +0 -6
- package/src/qingflow_mcp/tools/view_writer.py +44 -0
- package/skills/qingflow-cli/reference/examples/views/views_batch_full.example.json +0 -159
- package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +0 -42
|
@@ -7,7 +7,7 @@ Use these files as copyable bases after reading the matching resource document.
|
|
|
7
7
|
| Historical single-app schema probe | [schema/schema_single_app_form_standard.example.json](./schema/schema_single_app_form_standard.example.json) | Retired `--form-file` shape; use `builder app-form schema/get/validate/apply` for current declarations. |
|
|
8
8
|
| Historical multi-app schema probe | [schema/schema_multi_app_form_relation_layout.example.json](./schema/schema_multi_app_form_relation_layout.example.json) | Retired `--apps-file` shape; current systems create independent AppForms and then bind confirmed app keys. |
|
|
9
9
|
| Existing-app layout probe | [layout/layout_sections_full.example.json](./layout/layout_sections_full.example.json) | Historical layout-patch shape; current new and existing forms use the complete AppForm `spec.body`. |
|
|
10
|
-
| Views | [views/views_upsert_table_minimal.example.json](./views/views_upsert_table_minimal.example.json), [views/
|
|
10
|
+
| Views | [views/views_upsert_table_minimal.example.json](./views/views_upsert_table_minimal.example.json), [views/views_apply_v1alpha1.example.json](./views/views_apply_v1alpha1.example.json) | 单视图 JSON 对象,包含筛选、查询面板和视图绑定按钮;通过 `--app-key` 指定应用。 |
|
|
11
11
|
| Charts | [charts/charts_upsert_dashboard_starter.example.json](./charts/charts_upsert_dashboard_starter.example.json) | Standalone chart apply; portal usually creates missing charts inline. |
|
|
12
12
|
| Portal | [portal/portal_sections_standard_workbench.example.json](./portal/portal_sections_standard_workbench.example.json) | Standard workbench with inline QingBI charts and data views. |
|
|
13
13
|
| Associated resources | [associated/associated_resources_semantic_full.example.json](./associated/associated_resources_semantic_full.example.json) | Current-record matching through `match_mappings`. |
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "项目台账视图",
|
|
3
|
+
"type": "table",
|
|
4
|
+
"columns": [100, 101, 102, 103],
|
|
5
|
+
"data_permission": {"type": "custom", "scope": "dept"},
|
|
6
|
+
"sort": [{"que_id": 100, "ascending": false}],
|
|
7
|
+
"display": {"row_height": "compact", "pin_navigate": true},
|
|
8
|
+
"query_conditions": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"exact": false,
|
|
11
|
+
"hide_before_query": false,
|
|
12
|
+
"rows": [["项目名称", "负责人"], ["创建时间"]]
|
|
13
|
+
}
|
|
14
|
+
}
|
package/skills/qingflow-cli/reference/examples/views/views_upsert_table_minimal.example.json
CHANGED
|
@@ -1,42 +1,36 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"name": "示例业务操作视图_CLI模板",
|
|
3
|
+
"type": "table",
|
|
4
|
+
"columns": [100, 101, 102, 103],
|
|
5
|
+
"filters": [
|
|
6
|
+
{"que_id": 100, "operator": "neq", "value": "已关闭"}
|
|
7
|
+
],
|
|
8
|
+
"query_conditions": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"exact": false,
|
|
11
|
+
"hide_before_query": false,
|
|
12
|
+
"rows": [["主记录编号", "主记录名称"], ["状态", "负责人"]]
|
|
13
|
+
},
|
|
14
|
+
"action_buttons": [
|
|
3
15
|
{
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
"filters": [
|
|
10
|
-
{"field_name": "状态", "operator": "neq", "value": "已关闭"}
|
|
16
|
+
"text": "行内创建子记录",
|
|
17
|
+
"action": "add_data",
|
|
18
|
+
"target_app_key": "TARGET_APP_KEY",
|
|
19
|
+
"field_mappings": [
|
|
20
|
+
{"source_field": "数据ID", "target_field": "关联主记录"}
|
|
11
21
|
],
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
{"source_field": "数据ID", "target_field": "关联主记录"}
|
|
25
|
-
],
|
|
26
|
-
"default_values": {"目标状态字段": "REPLACE_WITH_TARGET_OPTION_LABEL"},
|
|
27
|
-
"placement": "list"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"text": "详情创建子记录",
|
|
31
|
-
"action": "add_data",
|
|
32
|
-
"target_app_key": "TARGET_APP_KEY",
|
|
33
|
-
"field_mappings": [
|
|
34
|
-
{"source_field": "数据ID", "target_field": "关联主记录"}
|
|
35
|
-
],
|
|
36
|
-
"default_values": {"目标状态字段": "REPLACE_WITH_TARGET_OPTION_LABEL"},
|
|
37
|
-
"placement": "detail"
|
|
38
|
-
}
|
|
39
|
-
]
|
|
22
|
+
"default_values": {"目标状态字段": "REPLACE_WITH_TARGET_OPTION_LABEL"},
|
|
23
|
+
"placement": "list"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"text": "详情创建子记录",
|
|
27
|
+
"action": "add_data",
|
|
28
|
+
"target_app_key": "TARGET_APP_KEY",
|
|
29
|
+
"field_mappings": [
|
|
30
|
+
{"source_field": "数据ID", "target_field": "关联主记录"}
|
|
31
|
+
],
|
|
32
|
+
"default_values": {"目标状态字段": "REPLACE_WITH_TARGET_OPTION_LABEL"},
|
|
33
|
+
"placement": "detail"
|
|
40
34
|
}
|
|
41
35
|
]
|
|
42
36
|
}
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
from enum import Enum
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any, Union
|
|
6
6
|
|
|
7
7
|
from pydantic import AliasChoices, Field, model_validator
|
|
8
8
|
|
|
@@ -134,6 +134,8 @@ class PublicViewType(str, Enum):
|
|
|
134
134
|
card = "card"
|
|
135
135
|
board = "board"
|
|
136
136
|
gantt = "gantt"
|
|
137
|
+
hierarchy = "hierarchy"
|
|
138
|
+
calendar = "calendar"
|
|
137
139
|
|
|
138
140
|
|
|
139
141
|
class PublicButtonTriggerAction(str, Enum):
|
|
@@ -390,9 +392,38 @@ class FlowConditionRulePatch(StrictModel):
|
|
|
390
392
|
return self
|
|
391
393
|
|
|
392
394
|
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _coerce_que_id(value: Any) -> Any:
|
|
398
|
+
"""字段引用归一:queId(后端字段唯一键)。接受 int / "123" / "field_123"(field_id 形态) → int;
|
|
399
|
+
其余形态原样返回交给 pydantic 报错(契约诚实:不支持显示名引用)。"""
|
|
400
|
+
if isinstance(value, bool) or isinstance(value, int):
|
|
401
|
+
return value
|
|
402
|
+
if isinstance(value, str):
|
|
403
|
+
stripped = value.strip()
|
|
404
|
+
if stripped.isdigit():
|
|
405
|
+
return int(stripped)
|
|
406
|
+
if stripped.startswith("field_") and stripped[6:].isdigit():
|
|
407
|
+
return int(stripped[6:])
|
|
408
|
+
return value
|
|
409
|
+
|
|
393
410
|
class ViewFilterRulePatch(StrictModel):
|
|
394
|
-
|
|
411
|
+
que_id: int = Field(validation_alias=AliasChoices("que_id", "queId", "field_id", "fieldId"))
|
|
395
412
|
operator: ViewFilterOperator = Field(default=ViewFilterOperator.eq, validation_alias=AliasChoices("operator", "op"))
|
|
413
|
+
|
|
414
|
+
@model_validator(mode="before")
|
|
415
|
+
@classmethod
|
|
416
|
+
def normalize_que_id(cls, value: Any) -> Any:
|
|
417
|
+
if isinstance(value, dict):
|
|
418
|
+
for alias in ("queId", "field_id", "fieldId"):
|
|
419
|
+
if alias in value and "que_id" not in value:
|
|
420
|
+
value = {**{k: v for k, v in value.items() if k != alias}, "que_id": value[alias]}
|
|
421
|
+
break
|
|
422
|
+
for alias in ("queId", "field_id", "fieldId"):
|
|
423
|
+
value.pop(alias, None) if alias != "que_id" else None
|
|
424
|
+
if isinstance(value, dict) and "que_id" in value:
|
|
425
|
+
return {**value, "que_id": _coerce_que_id(value["que_id"])}
|
|
426
|
+
return value
|
|
396
427
|
values: list[Any] = Field(default_factory=list)
|
|
397
428
|
|
|
398
429
|
@model_validator(mode="before")
|
|
@@ -405,11 +436,18 @@ class ViewFilterRulePatch(StrictModel):
|
|
|
405
436
|
if self.operator in {ViewFilterOperator.is_empty, ViewFilterOperator.not_empty}:
|
|
406
437
|
self.values = []
|
|
407
438
|
return self
|
|
408
|
-
if not self.values:
|
|
439
|
+
if not self.values and self.operator != ViewFilterOperator.in_:
|
|
440
|
+
# in+空值合法:选项/成员类字段"ANY 判型未选任何值"的存量形态
|
|
441
|
+
# (后端 judgeType=EQUAL_ANY+空值,实测回写稳定保留)
|
|
409
442
|
raise ValueError("view filter rule requires values")
|
|
410
443
|
return self
|
|
411
444
|
|
|
412
445
|
|
|
446
|
+
# 视图筛选双形态(与 visible_when/root_filter 的嵌组形态对齐):
|
|
447
|
+
# 扁平 [rule, ...] = 单组 AND(向后兼容);嵌套 [[rule,...],[rule,...]] = 组间 OR、组内 AND
|
|
448
|
+
ViewFilterGroupPatch = Union[ViewFilterRulePatch, list[ViewFilterRulePatch]]
|
|
449
|
+
|
|
450
|
+
|
|
413
451
|
class FieldSelector(StrictModel):
|
|
414
452
|
field_id: str | None = None
|
|
415
453
|
que_id: int | None = None
|
|
@@ -544,16 +582,14 @@ class VisibilityPatch(StrictModel):
|
|
|
544
582
|
|
|
545
583
|
@model_validator(mode="after")
|
|
546
584
|
def validate_shape(self) -> "VisibilityPatch":
|
|
547
|
-
|
|
548
|
-
|
|
585
|
+
# mode=specific + 空 selectors 是合法形态(auth=2 但未配置成员),回写保真;
|
|
586
|
+
# 不强制要求 specific 必须有 selectors
|
|
549
587
|
if self.mode != PublicVisibilityMode.specific and self.selectors.has_any_selector():
|
|
550
588
|
raise ValueError("selectors are only allowed when mode=specific")
|
|
551
589
|
if self.mode == PublicVisibilityMode.everyone and self.external_mode != PublicExternalVisibilityMode.workspace:
|
|
552
590
|
raise ValueError("mode=everyone requires external_mode=workspace")
|
|
553
591
|
if self.mode == PublicVisibilityMode.everyone and self.external_selectors.has_any_selector():
|
|
554
592
|
raise ValueError("external_selectors are not allowed when mode=everyone")
|
|
555
|
-
if self.external_mode == PublicExternalVisibilityMode.specific and not self.external_selectors.has_any_selector():
|
|
556
|
-
raise ValueError("external_mode=specific requires external_selectors")
|
|
557
593
|
if self.external_mode != PublicExternalVisibilityMode.specific and self.external_selectors.has_any_selector():
|
|
558
594
|
raise ValueError("external_selectors are only allowed when external_mode=specific")
|
|
559
595
|
return self
|
|
@@ -1137,6 +1173,149 @@ class ViewQueryConditionsPatch(StrictModel):
|
|
|
1137
1173
|
rows: list[list[str | int]] = Field(default_factory=list, validation_alias=AliasChoices("rows", "fields", "queryCondition"))
|
|
1138
1174
|
|
|
1139
1175
|
|
|
1176
|
+
class ViewDataPermissionPatch(StrictModel):
|
|
1177
|
+
"""数据权限形状(公开语义)→ dataPermissionType + dataScope。"""
|
|
1178
|
+
|
|
1179
|
+
type: str = Field(default="custom", validation_alias=AliasChoices("type", "permission_type", "permissionType"))
|
|
1180
|
+
scope: str = Field(default="all", validation_alias=AliasChoices("scope", "data_scope", "dataScope"))
|
|
1181
|
+
|
|
1182
|
+
@model_validator(mode="after")
|
|
1183
|
+
def validate_shape(self) -> "ViewDataPermissionPatch":
|
|
1184
|
+
if self.type not in {"config", "custom"}:
|
|
1185
|
+
raise ValueError("data_permission.type must be config or custom")
|
|
1186
|
+
if self.scope not in {"all", "oneself", "dept", "subordinate", "custom"}:
|
|
1187
|
+
raise ValueError("data_permission.scope must be one of all/oneself/dept/subordinate/custom")
|
|
1188
|
+
return self
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
class ViewSortRulePatch(StrictModel):
|
|
1192
|
+
"""排序规则(公开语义)→ viewgraphSorts + sortType=customSort。"""
|
|
1193
|
+
|
|
1194
|
+
que_id: int = Field(validation_alias=AliasChoices("que_id", "queId", "field_id", "fieldId"))
|
|
1195
|
+
ascending: bool = Field(default=True, validation_alias=AliasChoices("ascending", "asc", "beingSortAscend"))
|
|
1196
|
+
|
|
1197
|
+
@model_validator(mode="before")
|
|
1198
|
+
@classmethod
|
|
1199
|
+
def normalize_que_id(cls, value: Any) -> Any:
|
|
1200
|
+
if isinstance(value, dict):
|
|
1201
|
+
for alias in ("queId", "field_id", "fieldId"):
|
|
1202
|
+
if alias in value and "que_id" not in value:
|
|
1203
|
+
value = {**{k: v for k, v in value.items() if k != alias}, "que_id": value[alias]}
|
|
1204
|
+
break
|
|
1205
|
+
for alias in ("queId", "field_id", "fieldId"):
|
|
1206
|
+
value.pop(alias, None) if alias != "que_id" else None
|
|
1207
|
+
if isinstance(value, dict) and "que_id" in value:
|
|
1208
|
+
return {**value, "que_id": _coerce_que_id(value["que_id"])}
|
|
1209
|
+
return value
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
class ViewCardConfigPatch(StrictModel):
|
|
1213
|
+
"""卡片视图特有配置 → titleQue/frontCoverQueId/clippingMode/... 。"""
|
|
1214
|
+
|
|
1215
|
+
title_que_id: int | None = Field(default=None, validation_alias=AliasChoices("title_que_id", "titleQueId"))
|
|
1216
|
+
cover_que_id: int | None = Field(default=None, validation_alias=AliasChoices("cover_que_id", "coverQueId", "front_cover_que_id", "frontCoverQueId"))
|
|
1217
|
+
clipping_mode: str | None = Field(default=None, validation_alias=AliasChoices("clipping_mode", "clippingMode"))
|
|
1218
|
+
image_adaption: bool | None = Field(default=None, validation_alias=AliasChoices("image_adaption", "imageAdaption", "beingImageAdaption"))
|
|
1219
|
+
show_field_title: bool | None = Field(default=None, validation_alias=AliasChoices("show_field_title", "showFieldTitle", "beingShowQueTitle"))
|
|
1220
|
+
show_title: bool | None = Field(default=None, validation_alias=AliasChoices("show_title", "showTitle", "beingShowTitleQue"))
|
|
1221
|
+
show_cover: bool | None = Field(default=None, validation_alias=AliasChoices("show_cover", "showCover", "beingShowCover"))
|
|
1222
|
+
group_que_id: int | None = Field(default=None, validation_alias=AliasChoices("group_que_id", "groupQueId"))
|
|
1223
|
+
group_color: bool | None = Field(default=None, validation_alias=AliasChoices("group_color", "groupColor", "beingGroupColor"))
|
|
1224
|
+
|
|
1225
|
+
@model_validator(mode="after")
|
|
1226
|
+
def validate_shape(self) -> "ViewCardConfigPatch":
|
|
1227
|
+
if self.clipping_mode is not None and self.clipping_mode not in {"default", "fill"}:
|
|
1228
|
+
raise ValueError("card.clipping_mode must be default or fill")
|
|
1229
|
+
return self
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
class ViewGanttGroupPatch(StrictModel):
|
|
1233
|
+
que_id: int = Field(validation_alias=AliasChoices("que_id", "queId"))
|
|
1234
|
+
ordinal: int | None = Field(default=None)
|
|
1235
|
+
|
|
1236
|
+
|
|
1237
|
+
class ViewGanttDependencyPatch(StrictModel):
|
|
1238
|
+
predecessor_que_id: int = Field(validation_alias=AliasChoices("predecessor_que_id", "predecessorQueId"))
|
|
1239
|
+
dependency_que_id: int = Field(validation_alias=AliasChoices("dependency_que_id", "dependencyQueId"))
|
|
1240
|
+
start_start_option_id: int | None = Field(default=None, validation_alias=AliasChoices("start_start_option_id", "startStartOptionId"))
|
|
1241
|
+
start_end_option_id: int | None = Field(default=None, validation_alias=AliasChoices("start_end_option_id", "startEndOptionId"))
|
|
1242
|
+
end_start_option_id: int | None = Field(default=None, validation_alias=AliasChoices("end_start_option_id", "endStartOptionId"))
|
|
1243
|
+
end_end_option_id: int | None = Field(default=None, validation_alias=AliasChoices("end_end_option_id", "endEndOptionId"))
|
|
1244
|
+
|
|
1245
|
+
|
|
1246
|
+
class ViewGanttConfigPatch(StrictModel):
|
|
1247
|
+
"""甘特视图特有配置 → viewgraphGanttConfigVO。"""
|
|
1248
|
+
|
|
1249
|
+
title_que_id: int | None = Field(default=None, validation_alias=AliasChoices("title_que_id", "titleQueId"))
|
|
1250
|
+
start_que_id: int | None = Field(default=None, validation_alias=AliasChoices("start_que_id", "startQueId"))
|
|
1251
|
+
end_que_id: int | None = Field(default=None, validation_alias=AliasChoices("end_que_id", "endQueId"))
|
|
1252
|
+
default_time_dimension: str | None = Field(default=None, validation_alias=AliasChoices("default_time_dimension", "defaultTimeDimension"))
|
|
1253
|
+
auto_calibration: bool | None = Field(default=None, validation_alias=AliasChoices("auto_calibration", "autoCalibration", "beingAutoCalibration"))
|
|
1254
|
+
groups: list[ViewGanttGroupPatch] = Field(default_factory=list)
|
|
1255
|
+
dependencies: list[ViewGanttDependencyPatch] = Field(default_factory=list, validation_alias=AliasChoices("dependencies", "dependency"))
|
|
1256
|
+
|
|
1257
|
+
@model_validator(mode="after")
|
|
1258
|
+
def validate_shape(self) -> "ViewGanttConfigPatch":
|
|
1259
|
+
if self.default_time_dimension is not None and self.default_time_dimension not in {"day", "week", "month", "year"}:
|
|
1260
|
+
raise ValueError("gantt.default_time_dimension must be day, week, month, or year")
|
|
1261
|
+
if len(self.dependencies) > 1:
|
|
1262
|
+
raise ValueError("gantt.dependencies supports at most one dependency relation in v1alpha1")
|
|
1263
|
+
return self
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
class ViewHierarchyConfigPatch(StrictModel):
|
|
1267
|
+
"""层级视图特有配置 → viewgraphHierarchyConfigVO。"""
|
|
1268
|
+
|
|
1269
|
+
title_que_id: int = Field(validation_alias=AliasChoices("title_que_id", "titleQueId"))
|
|
1270
|
+
children_que_id: int = Field(validation_alias=AliasChoices("children_que_id", "childrenQueId"))
|
|
1271
|
+
root_filter: list[list[ViewFilterRulePatch]] = Field(
|
|
1272
|
+
default_factory=list,
|
|
1273
|
+
validation_alias=AliasChoices("root_filter", "rootFilter", "root_node_filter", "rootNodeFilter"),
|
|
1274
|
+
)
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
class ViewCalendarConfigPatch(StrictModel):
|
|
1278
|
+
"""日历视图特有配置 → viewgraphCalendarConfigVO。"""
|
|
1279
|
+
|
|
1280
|
+
title_que_id: int = Field(validation_alias=AliasChoices("title_que_id", "titleQueId"))
|
|
1281
|
+
date_que_id: int = Field(validation_alias=AliasChoices("date_que_id", "dateQueId"))
|
|
1282
|
+
color_que_id: int | None = Field(default=None, validation_alias=AliasChoices("color_que_id", "colorQueId"))
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
class ViewDisplayConfigPatch(StrictModel):
|
|
1286
|
+
"""展示杂项 → defaultRowHigh/beingPinNavigate/remark。"""
|
|
1287
|
+
|
|
1288
|
+
row_height: str | None = Field(default=None, validation_alias=AliasChoices("row_height", "rowHeight", "defaultRowHigh"))
|
|
1289
|
+
pin_navigate: bool | None = Field(default=None, validation_alias=AliasChoices("pin_navigate", "pinNavigate", "beingPinNavigate"))
|
|
1290
|
+
remark: str | None = Field(default=None)
|
|
1291
|
+
|
|
1292
|
+
@model_validator(mode="after")
|
|
1293
|
+
def validate_shape(self) -> "ViewDisplayConfigPatch":
|
|
1294
|
+
# 后端 defaultRowHigh 实际值域:compact/moderate/highest(high 为历史别名)
|
|
1295
|
+
if self.row_height is not None and self.row_height not in {"compact", "moderate", "highest", "high"}:
|
|
1296
|
+
raise ValueError("display.row_height must be compact, moderate, highest, or high")
|
|
1297
|
+
return self
|
|
1298
|
+
|
|
1299
|
+
|
|
1300
|
+
class ViewRecordConfigPatch(StrictModel):
|
|
1301
|
+
"""记录可见性开关 → beingAuditRecordVisible 等。"""
|
|
1302
|
+
|
|
1303
|
+
audit_visible: bool | None = Field(default=None, validation_alias=AliasChoices("audit_visible", "auditVisible", "beingAuditRecordVisible"))
|
|
1304
|
+
data_log_visible: bool | None = Field(default=None, validation_alias=AliasChoices("data_log_visible", "dataLogVisible", "beingDataLogVisible"))
|
|
1305
|
+
qrobot_record_visible: bool | None = Field(default=None, validation_alias=AliasChoices("qrobot_record_visible", "qrobotRecordVisible", "beingQrobotRecordVisible"))
|
|
1306
|
+
workflow_future_visible: bool | None = Field(default=None, validation_alias=AliasChoices("workflow_future_visible", "workflowFutureVisible", "beingWorkflowNodeFutureListVisible"))
|
|
1307
|
+
comment: bool | None = Field(default=None, validation_alias=AliasChoices("comment", "beingCommentStatus"))
|
|
1308
|
+
|
|
1309
|
+
|
|
1310
|
+
class ViewShareConfigPatch(StrictModel):
|
|
1311
|
+
"""分享与安全 → beingNeedPass/viewgraphPass/beingForceLogin/beingPrintStatus。"""
|
|
1312
|
+
|
|
1313
|
+
need_pass: bool | None = Field(default=None, validation_alias=AliasChoices("need_pass", "needPass", "beingNeedPass"))
|
|
1314
|
+
password: str | None = Field(default=None, validation_alias=AliasChoices("password", "pass", "viewgraphPass"))
|
|
1315
|
+
force_login: bool | None = Field(default=None, validation_alias=AliasChoices("force_login", "forceLogin", "beingForceLogin"))
|
|
1316
|
+
print_enabled: bool | None = Field(default=None, validation_alias=AliasChoices("print_enabled", "printEnabled", "beingPrintStatus"))
|
|
1317
|
+
|
|
1318
|
+
|
|
1140
1319
|
class ViewAssociatedResourcesPatch(StrictModel):
|
|
1141
1320
|
visible: bool = Field(default=True, validation_alias=AliasChoices("visible", "enabled", "asosChartVisible"))
|
|
1142
1321
|
limit_type: str | None = Field(default=None, validation_alias=AliasChoices("limit_type", "limitType"))
|
|
@@ -1239,17 +1418,30 @@ class ViewActionButtonPatch(StrictModel):
|
|
|
1239
1418
|
class ViewUpsertPatch(StrictModel):
|
|
1240
1419
|
name: str
|
|
1241
1420
|
view_key: str | None = Field(default=None, validation_alias=AliasChoices("view_key", "viewKey"))
|
|
1421
|
+
pinned: bool | None = Field(default=None, validation_alias=AliasChoices("pinned", "fixed", "is_pinned", "isPinned"))
|
|
1242
1422
|
type: PublicViewType
|
|
1243
|
-
columns: list[
|
|
1244
|
-
|
|
1245
|
-
filters: list[
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1423
|
+
columns: list[int] = Field(default_factory=list)
|
|
1424
|
+
group_que_id: int | None = Field(default=None, validation_alias=AliasChoices("group_que_id", "groupQueId"))
|
|
1425
|
+
filters: list["ViewFilterGroupPatch"] = Field(default_factory=list)
|
|
1426
|
+
start_que_id: int | None = Field(default=None, validation_alias=AliasChoices("start_que_id", "startQueId"))
|
|
1427
|
+
end_que_id: int | None = Field(default=None, validation_alias=AliasChoices("end_que_id", "endQueId"))
|
|
1428
|
+
title_que_id: int | None = Field(default=None, validation_alias=AliasChoices("title_que_id", "titleQueId"))
|
|
1249
1429
|
buttons: list["ViewButtonBindingPatch"] | None = None
|
|
1250
1430
|
action_buttons: list[ViewActionButtonPatch] | None = Field(default=None, validation_alias=AliasChoices("action_buttons", "actionButtons"))
|
|
1251
1431
|
action_buttons_mode: str = Field(default="merge", validation_alias=AliasChoices("action_buttons_mode", "actionButtonsMode", "button_mode", "buttonMode"))
|
|
1252
1432
|
visibility: VisibilityPatch | None = None
|
|
1433
|
+
data_permission: ViewDataPermissionPatch | None = Field(
|
|
1434
|
+
default=None,
|
|
1435
|
+
validation_alias=AliasChoices("data_permission", "dataPermission"),
|
|
1436
|
+
)
|
|
1437
|
+
sort: list[ViewSortRulePatch] | None = Field(default=None, validation_alias=AliasChoices("sort", "sort_rules", "sortRules"))
|
|
1438
|
+
card: ViewCardConfigPatch | None = Field(default=None)
|
|
1439
|
+
gantt: ViewGanttConfigPatch | None = Field(default=None)
|
|
1440
|
+
hierarchy: ViewHierarchyConfigPatch | None = Field(default=None)
|
|
1441
|
+
calendar: ViewCalendarConfigPatch | None = Field(default=None)
|
|
1442
|
+
display: ViewDisplayConfigPatch | None = Field(default=None)
|
|
1443
|
+
record: ViewRecordConfigPatch | None = Field(default=None)
|
|
1444
|
+
share: ViewShareConfigPatch | None = Field(default=None)
|
|
1253
1445
|
query_conditions: ViewQueryConditionsPatch | None = Field(default=None, validation_alias=AliasChoices("query_conditions", "queryConditions", "query_condition", "queryCondition"))
|
|
1254
1446
|
associated_resources: ViewAssociatedResourcesPatch | None = Field(
|
|
1255
1447
|
default=None,
|
|
@@ -1315,16 +1507,34 @@ class ViewUpsertPatch(StrictModel):
|
|
|
1315
1507
|
payload["type"] = "board"
|
|
1316
1508
|
elif normalized == "ganttview":
|
|
1317
1509
|
payload["type"] = "gantt"
|
|
1510
|
+
elif normalized == "hierarchyview":
|
|
1511
|
+
payload["type"] = "hierarchy"
|
|
1512
|
+
elif normalized == "calendarview":
|
|
1513
|
+
payload["type"] = "calendar"
|
|
1318
1514
|
return payload
|
|
1319
1515
|
|
|
1320
1516
|
@model_validator(mode="after")
|
|
1321
1517
|
def validate_shape(self) -> "ViewUpsertPatch":
|
|
1322
1518
|
if self.type in {PublicViewType.table, PublicViewType.card} and not self.columns:
|
|
1323
1519
|
raise ValueError("table/card views require columns")
|
|
1324
|
-
if self.type == PublicViewType.board and not self.
|
|
1325
|
-
raise ValueError("board view requires
|
|
1326
|
-
if self.type == PublicViewType.gantt and not (self.
|
|
1327
|
-
raise ValueError("gantt view requires
|
|
1520
|
+
if self.type == PublicViewType.board and not self.group_que_id:
|
|
1521
|
+
raise ValueError("board view requires group_que_id")
|
|
1522
|
+
if self.type == PublicViewType.gantt and not (self.start_que_id and self.end_que_id):
|
|
1523
|
+
raise ValueError("gantt view requires start_que_id and end_que_id")
|
|
1524
|
+
if self.type == PublicViewType.hierarchy:
|
|
1525
|
+
if self.hierarchy is None or not (self.hierarchy.title_que_id and self.hierarchy.children_que_id):
|
|
1526
|
+
raise ValueError("hierarchy view requires hierarchy.title_que_id and children_que_id")
|
|
1527
|
+
elif self.hierarchy is not None:
|
|
1528
|
+
raise ValueError("hierarchy config is only allowed on hierarchy views")
|
|
1529
|
+
if self.type == PublicViewType.calendar:
|
|
1530
|
+
if self.calendar is None:
|
|
1531
|
+
raise ValueError("calendar view requires calendar.title_que_id and date_que_id")
|
|
1532
|
+
elif self.calendar is not None:
|
|
1533
|
+
raise ValueError("calendar config is only allowed on calendar views")
|
|
1534
|
+
if self.type != PublicViewType.card and self.card is not None:
|
|
1535
|
+
raise ValueError("card config is only allowed on card views")
|
|
1536
|
+
if self.type != PublicViewType.gantt and self.gantt is not None:
|
|
1537
|
+
raise ValueError("gantt config is only allowed on gantt views")
|
|
1328
1538
|
normalized_mode = str(self.action_buttons_mode or "merge").strip().lower()
|
|
1329
1539
|
if normalized_mode not in {"merge", "replace"}:
|
|
1330
1540
|
raise ValueError("action_buttons_mode must be merge or replace")
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifestDescription": "面向 app_views_apply 的版本化公开视图契约。summary 提供最小声明,complete 覆盖当前可写能力,details 提供按配置域读取的自包含专项 Schema。后端已知但尚不可写的能力以说明性元数据列出,不会被误报为公开能力。",
|
|
3
|
+
"latestVersion": "v1alpha1",
|
|
4
|
+
"versions": {
|
|
5
|
+
"v1alpha1": {
|
|
6
|
+
"versionDescription": "AI 和 CLI/MCP 使用的 canonical views DSL。字段名、更新语义和约束与 app_views_apply 对齐;schema 返回会内联专项 Schema 的依赖定义。",
|
|
7
|
+
"apiVersion": "builder.qingflow.com/v1alpha1",
|
|
8
|
+
"applyDto": "v1alpha1",
|
|
9
|
+
"contractSource": "app_views_apply",
|
|
10
|
+
"contractVersion": "v1alpha1",
|
|
11
|
+
"enabledByDefault": true,
|
|
12
|
+
"schemaKinds": [
|
|
13
|
+
"summary",
|
|
14
|
+
"complete",
|
|
15
|
+
"display",
|
|
16
|
+
"data-permission",
|
|
17
|
+
"operation-permission",
|
|
18
|
+
"query-condition",
|
|
19
|
+
"button-config"
|
|
20
|
+
],
|
|
21
|
+
"schemaFiles": {
|
|
22
|
+
"summary": "v1alpha1/summary.json",
|
|
23
|
+
"complete": "v1alpha1/complete.json",
|
|
24
|
+
"display": "v1alpha1/details/display.json",
|
|
25
|
+
"data-permission": "v1alpha1/details/data-permission.json",
|
|
26
|
+
"operation-permission": "v1alpha1/details/operation-permission.json",
|
|
27
|
+
"query-condition": "v1alpha1/details/query-condition.json",
|
|
28
|
+
"button-config": "v1alpha1/details/button-config.json"
|
|
29
|
+
},
|
|
30
|
+
"viewTypes": [
|
|
31
|
+
"table",
|
|
32
|
+
"card",
|
|
33
|
+
"board",
|
|
34
|
+
"gantt",
|
|
35
|
+
"hierarchy",
|
|
36
|
+
"calendar"
|
|
37
|
+
],
|
|
38
|
+
"backendKnownUnsupportedViewTypes": [],
|
|
39
|
+
"status": "active"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|