docs(spec): 修复 spec 审查问题 — cascade/validation 标注为修改已有字段,dashboard 标注平台依赖
- CRITICAL: 明确 dashboard widgets 需要平台 manifest.rs 扩展 - HIGH: cascade/validation/visible_when 均改为"已有字段追加属性" - HIGH: visible_when 提供完整 TOML 语法 - MEDIUM: 模板引擎说明(Handlebars)和关系解析机制 - MEDIUM: itops validation 补充字段上下文 - MEDIUM: itops dashboard 明确插入位置
This commit is contained in:
@@ -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<Vec<PluginWidget>>` 字段
|
||||
> 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` 字段 + 前端渲染组件。
|
||||
|
||||
Reference in New Issue
Block a user