# freelance + itops 插件增强设计规格 > 日期: 2026-04-20 > 来源: 多专家头脑风暴(UX专家 + 业务顾问 + 运维专家 + 财务专家) > 状态: Draft > 前置: `docs/superpowers/specs/2026-04-19-shantou-zhijie-it-services-plugins-design.md` --- ## 1. 背景与动机 当前插件是「数据录入系统」,不是「赚钱工具」。一人 IT 服务公司的核心痛点: 1. **钱从哪里来?** — 商机跟进靠人记,没有自动提醒、没有漏斗分析 2. **项目做到哪了?** — 任务状态和工时手动填,跟合同金额/应收款脱节 3. **钱收回来了吗?** — 报价→合同→开票→收款割裂,没有串联 4. **运维服务会不会忘?** — 巡检计划写了没人催,SLA 超时了才知道 5. **税和利润算不清?** — 收支分散在不同表里,月底做账要手动汇总 **问题根因:** 平台已有 trigger_events、settings、templates、cascade_from、visible_when、validation 六大能力,但两个插件完全没有使用。 **改进目标:** 纯插件层增强,三层递进: - Layer 1: 智能业务引擎 — 让系统主动驱动用户做事 - Layer 2: 仪表盘重构 — 一个页面掌控全局 - Layer 3: 专业输出 — 一键生成报价单/发票/合同 PDF --- ## 2. Layer 1: 智能业务引擎 — freelance 插件 ### 2.1 Settings(插件配置页) 一次性配置公司信息和业务偏好,后续自动生效: ```toml [settings] # ── 基本信息 ── [[settings.fields]] name = "company_name" display_name = "公司名称" field_type = "text" required = true group = "基本信息" [[settings.fields]] name = "currency_symbol" display_name = "货币符号" field_type = "text" default_value = "¥" group = "基本信息" # ── 财务 ── [[settings.fields]] name = "default_tax_rate" display_name = "默认税率(%)" field_type = "number" default_value = 6 range = [0.0, 100.0] group = "财务" # ── 提醒 ── [[settings.fields]] name = "payment_reminder_days" display_name = "收款提前提醒(天)" field_type = "number" default_value = 3 range = [1.0, 30.0] group = "提醒" [[settings.fields]] name = "notify_contract_expiring" display_name = "合同到期提醒" field_type = "boolean" default_value = true group = "提醒" [[settings.fields]] name = "notify_payment_overdue" display_name = "逾期收款提醒" field_type = "boolean" default_value = true group = "提醒" [[settings.fields]] name = "notify_opportunity_followup" display_name = "商机跟进提醒" field_type = "boolean" default_value = true group = "提醒" ``` ### 2.2 Trigger Events(自动事件驱动) 关键操作时自动发通知,把"人找事"变"事找人": ```toml [[trigger_events]] name = "opportunity_stage_changed" display_name = "商机阶段变更" description = "商机阶段发生变化时通知,特别是成交或失败" entity = "opportunity" on = "update" [[trigger_events]] name = "contract_status_changed" display_name = "合同状态变更" description = "合同状态变化时检查到期预警" entity = "contract" on = "update" [[trigger_events]] name = "invoice_status_changed" display_name = "发票状态变更" description = "发票状态变化时检查逾期收款" entity = "invoice" on = "update" [[trigger_events]] name = "task_status_changed" display_name = "任务状态变更" description = "任务完成或取消时通知" entity = "task" on = "update" [[trigger_events]] name = "expense_created" display_name = "新支出记录" description = "记录新支出时通知" entity = "expense" on = "create" ``` ### 2.3 Cascade(智能联动下拉) 选客户后自动过滤其关联数据。以下均为**已有字段追加 cascade 属性**,不是新增字段: **contract 实体 — 已有 opportunity_id 字段追加:** ```toml cascade_from = "client_id" cascade_filter = "client_id" ``` **contract 实体 — 已有 quote_id 字段追加:** ```toml cascade_from = "client_id" cascade_filter = "client_id" ``` **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 cascade_from = "project_id" cascade_filter = "project_id" ``` ### 2.4 Visible When(条件显示) 只在有意义时才显示字段。以下为**已有字段追加 visible_when 属性**: **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(字段校验) **已有字段追加 validation 属性**,不是新增字段: **client 实体 — 已有 email 字段追加:** ```toml validation = { pattern = "^[^@]+@[^@]+\\.[^@]+$", message = "请输入有效的邮箱地址" } ``` **client 实体 — 已有 phone 字段追加:** ```toml validation = { pattern = "^1[3-9]\\d{9}$", message = "请输入有效的手机号" } ``` --- ## 3. Layer 2: 仪表盘重构 — freelance 插件 将占位符仪表盘升级为真正的指挥中心。通过 `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]] type = "dashboard" label = "工作台" icon = "DashboardOutlined" # ── 财务概览卡片 ── [[ui.pages.widgets]] type = "stat_cards" label = "财务概览" cards = [ { entity = "invoice", aggregate = "sum", field = "amount", filter = "type == 'payment' && status != 'overdue'", label = "本月收入", icon = "rise", color = "green" }, { entity = "expense", aggregate = "sum", field = "amount", label = "本月支出", icon = "fall", color = "red" }, { entity = "invoice", aggregate = "sum", field = "amount", filter = "status == 'overdue' || status == 'pending'", label = "应收总额", icon = "dollar", color = "orange" }, { entity = "invoice", aggregate = "count", filter = "status == 'overdue'", label = "逾期笔数", icon = "warning", color = "red" } ] # ── 紧急待办 ── [[ui.pages.widgets]] type = "action_list" label = "紧急待办" max_items = 5 queries = [ { entity = "invoice", filter = "status == 'overdue'", label_field = "invoice_number", subtitle_field = "amount", action = "查看", icon = "warning" }, { entity = "task", filter = "status != 'done' && status != 'cancelled'", sort = "due_date asc", label_field = "title", subtitle_field = "due_date", action = "处理", icon = "clock" }, { entity = "contract", filter = "status == 'active'", sort = "end_date asc", label_field = "title", subtitle_field = "end_date", action = "续约", icon = "file-text" }, { entity = "opportunity", filter = "next_follow_up <= today", label_field = "title", subtitle_field = "next_follow_up", action = "跟进", icon = "phone" } ] # ── 商机漏斗 ── [[ui.pages.widgets]] type = "funnel" label = "商机漏斗" entity = "opportunity" lane_field = "stage" value_field = "estimated_amount" lane_order = ["visit", "requirement", "quote", "negotiation", "won", "lost"] # ── 活跃项目卡片 ── [[ui.pages.widgets]] type = "card_list" label = "活跃项目" entity = "project" filter = "status == 'in_progress'" max_items = 4 title_field = "name" subtitle_field = "contract_amount" tags = ["business_type", "status"] ``` **依赖:** 数据源来自平台已有的聚合 API(`/count`、`/aggregate`)。Filter 表达式使用平台过滤 DSL(`==`, `!=`, `||`, `&&`, `<=`)。 --- ## 4. Layer 3: 专业输出 — freelance 插件 一键生成专业 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 报价单模板 ```toml [[templates]] name = "quote_pdf" display_name = "报价单" entity = "quote" format = "pdf" template_html = """

报价单 {{quote_number}}

客户:{{client.name}} | 有效期至:{{valid_until}}

{{#each lines}} {{/each}}
项目描述数量单价金额
{{item_name}}{{description}}{{quantity}}{{unit_price}}{{amount}}

小计:{{subtotal}} | 税率:{{tax_rate}}% | 总计:{{total_amount}}

""" ``` ### 4.2 发票模板 ```toml [[templates]] name = "invoice_pdf" display_name = "发票" entity = "invoice" format = "pdf" template_html = """

发票 {{invoice_number}}

客户:{{client.name}}
类型:{{type}}
开票日期:{{issue_date}}
到期日:{{due_date}}
¥{{amount}}

状态:{{status}}

备注:{{notes}}

""" ``` ### 4.3 合同模板 ```toml [[templates]] name = "contract_pdf" display_name = "合同" entity = "contract" format = "pdf" template_html = """

{{title}}

合同编号:{{contract_number}}

甲方:{{client.name}}

合同金额:¥{{amount}} | 已付:¥{{paid_amount}}

期限:{{start_date}} 至 {{end_date}}

付款条款:{{payment_terms}}

备注:{{notes}}

甲方签章
乙方签章
""" ``` --- ## 5. itops 插件增强 ### 5.1 Settings ```toml [settings] [[settings.fields]] name = "default_sla_response" display_name = "默认SLA响应时间(小时)" field_type = "number" default_value = 8 range = [1.0, 72.0] group = "SLA" [[settings.fields]] name = "default_sla_resolve" display_name = "默认SLA解决时间(小时)" field_type = "number" default_value = 48 range = [1.0, 168.0] group = "SLA" [[settings.fields]] name = "notify_sla_breach" display_name = "SLA超标提醒" field_type = "boolean" default_value = true group = "提醒" [[settings.fields]] name = "notify_check_due" display_name = "巡检到期提醒" field_type = "boolean" default_value = true group = "提醒" ``` ### 5.2 Trigger Events ```toml [[trigger_events]] name = "ticket_created" display_name = "新工单" description = "创建工单时开始SLA计时并通知" entity = "ticket" on = "create" [[trigger_events]] name = "ticket_status_changed" display_name = "工单状态变更" description = "工单状态变化时检查SLA是否达标" entity = "ticket" on = "update" [[trigger_events]] name = "contract_status_changed" display_name = "维保合同状态变更" description = "合同状态变化时检查到期预警" entity = "service_contract" on = "update" [[trigger_events]] name = "check_plan_updated" display_name = "巡检计划更新" description = "巡检计划更新时检查下次巡检日期" entity = "check_plan" on = "update" ``` ### 5.3 Cascade **已有字段追加 cascade 属性**,不是新增字段: **ticket 实体 — 已有 contract_id 字段追加:** ```toml cascade_from = "client_id" cascade_filter = "client_id" ``` **check_record 实体 — 已有 contract_id 字段追加:** ```toml cascade_from = "plan_id" cascade_filter = "contract_id" ``` ### 5.4 Visible When **已有字段追加 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 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" label = "运维概览" icon = "DashboardOutlined" [[ui.pages.widgets]] type = "stat_cards" label = "运维概览" cards = [ { entity = "service_contract", aggregate = "count", filter = "status == 'active'", label = "活跃合同", icon = "file-text", color = "blue" }, { entity = "ticket", aggregate = "count", filter = "status == 'open' || status == 'in_progress'", label = "待处理工单", icon = "tool", color = "orange" }, { entity = "ticket", aggregate = "count", filter = "status == 'resolved'", label = "已解决工单", icon = "check-circle", color = "green" }, { entity = "check_plan", aggregate = "count", filter = "status == 'active'", label = "活跃巡检", icon = "schedule", color = "blue" } ] [[ui.pages.widgets]] type = "action_list" label = "紧急待办" max_items = 5 queries = [ { entity = "ticket", filter = "status == 'open'", sort = "priority asc", label_field = "title", subtitle_field = "type", action = "处理", icon = "warning" }, { entity = "service_contract", filter = "status == 'active'", sort = "end_date asc", label_field = "name", subtitle_field = "end_date", action = "续约", icon = "file-text" }, { entity = "check_plan", filter = "status == 'active'", sort = "next_check_date asc", label_field = "name", subtitle_field = "next_check_date", action = "巡检", icon = "schedule" } ] ``` ### 5.7 Template(维保合同 PDF) ```toml [[templates]] name = "service_contract_pdf" display_name = "维保合同" entity = "service_contract" format = "pdf" template_html = """

{{name}}

合同编号:{{contract_number}}

客户:{{client.name}}
合同金额:¥{{amount}}
期限:{{start_date}} 至 {{end_date}}
状态:{{status}}
SLA 承诺:响应 {{sla_response_hours}} 小时内 / 解决 {{sla_resolve_hours}} 小时内

服务范围:{{service_scope}}

付款条款:{{payment_terms}}

备注:{{notes}}

甲方签章
乙方签章
""" ``` --- ## 6. 改进汇总 | 层次 | 能力 | freelance | itops | |------|------|-----------|-------| | Layer 1 | settings | 7 个配置项(公司名/税率/提醒偏好) | 4 个配置项(SLA默认值/提醒偏好) | | Layer 1 | trigger_events | 5 个事件(商机/合同/发票/任务/支出) | 4 个事件(工单/合同/巡检) | | Layer 1 | cascade | 4 处联动(合同/发票/工时表单) | 2 处联动(工单/巡检记录) | | Layer 1 | visible_when | 4 个条件字段 | 6 个条件字段 | | Layer 1 | validation | 2 个校验(邮箱/手机) | 1 个校验(合同编号格式) | | Layer 2 | dashboard widgets | 财务卡片+紧急待办+商机漏斗+项目卡片 | 运维卡片+紧急待办 | | Layer 3 | templates | 3 个 PDF(报价单/发票/合同) | 1 个 PDF(维保合同) | **总计:** 2 个插件 × 3 层增强,从「数据录入」升级为「赚钱工具」。 --- ## 7. 实施优先级 ``` P1: freelance Layer 1(settings + trigger_events + cascade + visible_when + validation) P2: itops Layer 1(settings + trigger_events + cascade + visible_when + validation) P3: freelance Layer 3(3 个 PDF 模板) P4: itops Layer 3(维保合同 PDF 模板) P5: freelance Layer 2(仪表盘 widgets) P6: itops Layer 2(仪表盘 widgets) ``` P1-P4 是纯 plugin.toml 改动(给已有字段追加 cascade/visible_when/validation 属性,以及新增 settings/trigger_events/templates 段落),可立即实施。P5-P6 的仪表盘 widgets 需要平台层配合:扩展 `manifest.rs` 的 `PluginPageType::Dashboard` 支持 `widgets` 字段 + 前端渲染组件。