From bcc6662add80765bf51689b9210b8996428857e0 Mon Sep 17 00:00:00 2001 From: iven Date: Mon, 20 Apr 2026 00:50:29 +0800 Subject: [PATCH] =?UTF-8?q?docs(spec):=20=E4=BF=AE=E5=A4=8D=20spec=20?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5=E9=97=AE=E9=A2=98=20=E2=80=94=20cascade/vali?= =?UTF-8?q?dation=20=E6=A0=87=E6=B3=A8=E4=B8=BA=E4=BF=AE=E6=94=B9=E5=B7=B2?= =?UTF-8?q?=E6=9C=89=E5=AD=97=E6=AE=B5=EF=BC=8Cdashboard=20=E6=A0=87?= =?UTF-8?q?=E6=B3=A8=E5=B9=B3=E5=8F=B0=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CRITICAL: 明确 dashboard widgets 需要平台 manifest.rs 扩展 - HIGH: cascade/validation/visible_when 均改为"已有字段追加属性" - HIGH: visible_when 提供完整 TOML 语法 - MEDIUM: 模板引擎说明(Handlebars)和关系解析机制 - MEDIUM: itops validation 补充字段上下文 - MEDIUM: itops dashboard 明确插入位置 --- ...eelance-itops-plugin-enhancement-design.md | 195 ++++++++++-------- 1 file changed, 105 insertions(+), 90 deletions(-) diff --git a/docs/superpowers/specs/2026-04-20-freelance-itops-plugin-enhancement-design.md b/docs/superpowers/specs/2026-04-20-freelance-itops-plugin-enhancement-design.md index 4341fe8..8410f79 100644 --- a/docs/superpowers/specs/2026-04-20-freelance-itops-plugin-enhancement-design.md +++ b/docs/superpowers/specs/2026-04-20-freelance-itops-plugin-enhancement-design.md @@ -133,91 +133,73 @@ on = "create" ### 2.3 Cascade(智能联动下拉) -选客户后自动过滤其关联数据: +选客户后自动过滤其关联数据。以下均为**已有字段追加 cascade 属性**,不是新增字段: -**contract 实体新增:** +**contract 实体 — 已有 opportunity_id 字段追加:** ```toml - # client_id 选择后,opportunity_id 自动过滤只显示该客户的商机 - [[schema.entities.fields]] - name = "opportunity_id" - field_type = "uuid" - display_name = "关联商机" - ref_entity = "opportunity" - cascade_from = "client_id" - cascade_filter = "client_id" - - # client_id 选择后,quote_id 自动过滤只显示该客户的报价 - [[schema.entities.fields]] - name = "quote_id" - field_type = "uuid" - display_name = "关联报价" - ref_entity = "quote" cascade_from = "client_id" cascade_filter = "client_id" ``` -**invoice 实体新增:** +**contract 实体 — 已有 quote_id 字段追加:** ```toml - # client_id 选择后,project_id 自动过滤 - [[schema.entities.fields]] - name = "project_id" - field_type = "uuid" - display_name = "关联项目" - ref_entity = "project" - cascade_from = "client_id" - cascade_filter = "client_id" - - # client_id 选择后,contract_id 自动过滤 - [[schema.entities.fields]] - name = "contract_id" - field_type = "uuid" - display_name = "关联合同" - ref_entity = "contract" cascade_from = "client_id" cascade_filter = "client_id" ``` -**time_entry 实体新增:** +**invoice 实体 — 已有 project_id 字段追加:** +```toml + cascade_from = "client_id" + cascade_filter = "client_id" +``` + +**invoice 实体 — 已有 contract_id 字段追加:** +```toml + cascade_from = "client_id" + cascade_filter = "client_id" +``` + +**time_entry 实体 — 已有 task_id 字段追加:** ```toml - # project_id 选择后,task_id 自动过滤只显示该项目的任务 - [[schema.entities.fields]] - name = "task_id" - field_type = "uuid" - display_name = "关联任务" - ui_widget = "entity_select" - ref_entity = "task" - ref_label_field = "title" - ref_search_fields = ["title"] cascade_from = "project_id" cascade_filter = "project_id" ``` ### 2.4 Visible When(条件显示) -只在有意义时才显示字段: +只在有意义时才显示字段。以下为**已有字段追加 visible_when 属性**: -| 实体 | 字段 | 条件 | -|------|------|------| -| invoice | payment_date | `status == 'paid' \|\| status == 'partial'` | -| contract | paid_amount | `status != 'drafting'` | -| task | actual_hours | `status != 'todo'` | -| quote | total_amount | `status != 'draft'` | +**invoice 实体 — 已有 payment_date 字段追加:** +```toml + visible_when = "status == 'paid' || status == 'partial'" +``` + +**contract 实体 — 已有 paid_amount 字段追加:** +```toml + visible_when = "status != 'drafting'" +``` + +**task 实体 — 已有 actual_hours 字段追加:** +```toml + visible_when = "status != 'todo'" +``` + +**quote 实体 — 已有 total_amount 字段追加:** +```toml + visible_when = "status != 'draft'" +``` ### 2.5 Validation(字段校验) -```toml - # client 实体 - email 字段增加校验 - [[schema.entities.fields]] - name = "email" - field_type = "string" - display_name = "邮箱" - validation = { pattern = "^[^@]+@[^@]+\\.[^@]+$", message = "请输入有效的邮箱地址" } +**已有字段追加 validation 属性**,不是新增字段: - # client 实体 - phone 字段增加校验 - [[schema.entities.fields]] - name = "phone" - field_type = "string" - display_name = "电话" +**client 实体 — 已有 email 字段追加:** +```toml + validation = { pattern = "^[^@]+@[^@]+\\.[^@]+$", message = "请输入有效的邮箱地址" } +``` + +**client 实体 — 已有 phone 字段追加:** +```toml validation = { pattern = "^1[3-9]\\d{9}$", message = "请输入有效的手机号" } ``` @@ -225,7 +207,15 @@ on = "create" ## 3. Layer 2: 仪表盘重构 — freelance 插件 -将占位符仪表盘升级为真正的指挥中心。通过 `widgets` 声明告诉平台该展示什么: +将占位符仪表盘升级为真正的指挥中心。通过 `widgets` 声明告诉平台该展示什么。 + +> **平台依赖:** 仪表盘 widgets 需要平台层配合: +> 1. `manifest.rs` 的 `PluginPageType::Dashboard` 需要新增 `widgets: Option>` 字段 +> 2. 定义 `PluginWidget` 枚举(stat_cards/action_list/funnel/card_list 类型) +> 3. 更新 TOML 解析和验证逻辑 +> 4. 前端解析 `widgets` 声明并渲染对应组件 +> +> 因此 P5/P6 **不是纯 plugin.toml 改动**,需要平台+前端联合实施。以下 widgets 声明作为设计参考,实施时需先完成平台侧支持。 ```toml [[ui.pages]] @@ -277,13 +267,20 @@ icon = "DashboardOutlined" tags = ["business_type", "status"] ``` -**依赖:** 仪表盘 widgets 渲染需要前端配合解析 `widgets` 声明。数据源来自平台已有的聚合 API(`/count`、`/aggregate`)。 +**依赖:** 数据源来自平台已有的聚合 API(`/count`、`/aggregate`)。Filter 表达式使用平台过滤 DSL(`==`, `!=`, `||`, `&&`, `<=`)。 --- ## 4. Layer 3: 专业输出 — freelance 插件 -一键生成专业 PDF,替代手动排 Word: +一键生成专业 PDF,替代手动排 Word。 + +> **模板引擎说明:** +> - 语法基于 Handlebars(`{{field}}`, `{{#each relation}}...{{/each}}`) +> - 当前实体字段直接可用:`{{amount}}`, `{{status}}` +> - 关系字段解析:`{{client.name}}` 表示通过 `client_id` 引用的 client 实体的 name 字段,渲染器需自动解析 +> - `{{#each lines}}` 用于一对多关系(如 quote → quote_line),渲染器查询子实体并遍历 +> - 平台需要实现 PDF 渲染管道:TOML 模板 → Handlebars 渲染(注入数据)→ HTML → wkhtmltopdf/浏览器打印 → PDF ### 4.1 报价单模板 @@ -464,49 +461,67 @@ on = "update" ### 5.3 Cascade +**已有字段追加 cascade 属性**,不是新增字段: + +**ticket 实体 — 已有 contract_id 字段追加:** ```toml -# ticket 实体:选客户后过滤其维保合同 - [[schema.entities.fields]] - name = "contract_id" - field_type = "uuid" - display_name = "维保合同" - ui_widget = "entity_select" - ref_entity = "service_contract" - ref_label_field = "name" - ref_search_fields = ["name"] cascade_from = "client_id" cascade_filter = "client_id" +``` -# check_record 实体:选巡检计划后过滤合同 - [[schema.entities.fields]] - name = "contract_id" - field_type = "uuid" - display_name = "维保合同" - ref_entity = "service_contract" +**check_record 实体 — 已有 contract_id 字段追加:** +```toml cascade_from = "plan_id" cascade_filter = "contract_id" ``` ### 5.4 Visible When -| 实体 | 字段 | 条件 | -|------|------|------| -| ticket | resolution | `status == 'resolved' \|\| status == 'closed'` | -| ticket | responded_at | `status != 'open'` | -| ticket | resolved_at | `status == 'resolved' \|\| status == 'closed'` | -| ticket | closed_at | `status == 'closed'` | -| check_record | issues_found | `result == 'abnormal'` | -| check_record | actions_taken | `result == 'abnormal'` | +**已有字段追加 visible_when 属性**: + +**ticket 实体 — 已有 resolution 字段追加:** +```toml + visible_when = "status == 'resolved' || status == 'closed'" +``` + +**ticket 实体 — 已有 responded_at 字段追加:** +```toml + visible_when = "status != 'open'" +``` + +**ticket 实体 — 已有 resolved_at 字段追加:** +```toml + visible_when = "status == 'resolved' || status == 'closed'" +``` + +**ticket 实体 — 已有 closed_at 字段追加:** +```toml + visible_when = "status == 'closed'" +``` + +**check_record 实体 — 已有 issues_found 字段追加:** +```toml + visible_when = "result == 'abnormal'" +``` + +**check_record 实体 — 已有 actions_taken 字段追加:** +```toml + visible_when = "result == 'abnormal'" +``` ### 5.5 Validation +**已有字段追加 validation 属性**: + +**service_contract 实体 — 已有 contract_number 字段追加:** ```toml - # service_contract - contract_number 格式校验 validation = { pattern = "^SC-\\d{4}-\\d{4}$", message = "格式:SC-YYYY-NNNN" } ``` ### 5.6 Dashboard +> **同 Layer 2 说明:** widgets 需要平台层配合(manifest.rs 扩展 + 前端渲染),非纯 plugin.toml 改动。此仪表盘页面**插入到现有页面列表最前面**,现有 4 个页面保持不变。 + ```toml [[ui.pages]] type = "dashboard" @@ -606,4 +621,4 @@ P5: freelance Layer 2(仪表盘 widgets) P6: itops Layer 2(仪表盘 widgets) ``` -P1-P4 是纯 plugin.toml 改动,可立即实施。P5-P6 的仪表盘 widgets 需要前端配合解析 widgets 声明。 +P1-P4 是纯 plugin.toml 改动(给已有字段追加 cascade/visible_when/validation 属性,以及新增 settings/trigger_events/templates 段落),可立即实施。P5-P6 的仪表盘 widgets 需要平台层配合:扩展 `manifest.rs` 的 `PluginPageType::Dashboard` 支持 `widgets` 字段 + 前端渲染组件。