feat(sdlc): cockpit driving mode — conversation timeline + agent chat
This commit is contained in:
parent
4a4bd313e7
commit
bd0890a394
159
docs/sdlc-architecture.md
Normal file
159
docs/sdlc-architecture.md
Normal file
@ -0,0 +1,159 @@
|
||||
# 开发产线 (SDLC Pipeline) 架构概览
|
||||
|
||||
## 客户可以输入什么
|
||||
|
||||
开发产线的入口是「项目管理」,客户分多阶段输入:
|
||||
|
||||
### 项目创建阶段
|
||||
- 项目名称、描述、项目类型 (web_app / mobile / api_service)
|
||||
- 技术栈配置 (JSON: 框架、语言、数据库等)
|
||||
- 代码仓库 URL
|
||||
- 关联的 Pipeline 定义 ID(选哪个产线模板)
|
||||
|
||||
### 迭代创建阶段
|
||||
- 迭代名称、迭代类型 (new_feature / bugfix / refactor / upgrade)
|
||||
- 迭代范围 (JSON: 功能点、用户故事)
|
||||
- 优先级 (1-10)
|
||||
- 创建迭代时**自动触发 Pipeline 任务**(project.py:auto-submit)
|
||||
|
||||
### 各阶段人工交互输入
|
||||
| 阶段 | 步骤 | 输入内容 |
|
||||
|------|------|----------|
|
||||
| 需求 | requirement_gathering | 需求文档表单 |
|
||||
| 需求 | requirement_review | 审批 (approve/reject) |
|
||||
| 设计 | table_design | 确认 LLM 生成的表结构 |
|
||||
| 设计 | crud_design | 确认 LLM 生成的 CRUD 定义 |
|
||||
| 设计 | api_design | 确认 LLM 生成的 API 规范 |
|
||||
| 设计 | design_review | 架构负责人审批 |
|
||||
| 开发 | code_review | 高级开发审批代码 |
|
||||
| 开发 | code_fix | 提交修复说明 |
|
||||
| 测试 | test_plan_create | 编写测试方案 |
|
||||
| 测试 | bug_fix | 修复说明 + commit SHA |
|
||||
| 测试 | acceptance_test | 产品经理验收 |
|
||||
| 部署 | deploy_env_collect | SSH 信息 + 数据库连接 |
|
||||
| 部署 | deploy_test_approve | 发布经理确认 |
|
||||
| 运维 | incident_response | 故障处理方案 |
|
||||
| 升级 | upgrade_plan | 新版本规划 |
|
||||
|
||||
### 统一输入形式(驾驶舱模式)
|
||||
- **正文输入**:多行文本框,支持自然语言
|
||||
- **文件附件**:上传需求文档、设计稿、代码文件等
|
||||
- 类似与大模型对话的交互体验
|
||||
|
||||
## 客户应该看到什么
|
||||
|
||||
### 仪表盘驾驶舱
|
||||
- 项目总数 / 活跃数
|
||||
- 活跃迭代数
|
||||
- 待处理 Bug(按严重程度分布:critical/major/minor/trivial)
|
||||
- 待审批数
|
||||
- 最近 5 个迭代及进度条
|
||||
|
||||
### 时间线面板
|
||||
- 产线步骤的线性时间线
|
||||
- 实时状态更新(completed/running/pending/waiting/failed)
|
||||
- 点击步骤展开详情(产物、日志、审批表单)
|
||||
- 按阶段分组(需求/设计/开发/测试/部署/运维)
|
||||
|
||||
### 对话面板
|
||||
- Agent ↔ 用户对话历史
|
||||
- Agent 自动推送:分析结果、设计产出预览、测试结果
|
||||
- 用户在输入框中回复、审批、补充信息
|
||||
- 系统事件通知(步骤开始/完成)
|
||||
|
||||
### 步骤详情弹窗
|
||||
- 步骤描述和配置
|
||||
- 输入/输出 artifact(JSON 树形查看)
|
||||
- 执行日志
|
||||
- human_task:表单 + 提交按钮
|
||||
- approval_gate:approve / reject 按钮
|
||||
- failed:错误详情 + 重试
|
||||
|
||||
### 项目管理页面
|
||||
- 项目列表:状态、类型、关联产线
|
||||
- 迭代列表:状态流转 (planning → in_progress → testing → completed)
|
||||
- Bug 管理:严重程度、状态、关联迭代、提交人类型
|
||||
|
||||
### 测试/部署页面
|
||||
- 测试用例列表、结果统计
|
||||
- 测试计划、部署环境配置
|
||||
|
||||
## 产线信息交互
|
||||
|
||||
当前开发产线定义了 **7 个阶段、28 个步骤类型**:
|
||||
|
||||
| 阶段 | 步骤数 | 自动 | 人工 | 审批 |
|
||||
|------|--------|------|------|------|
|
||||
| Phase 1: 需求 | 2 | 0 | 1 | 1 |
|
||||
| Phase 2: 设计 | 4 | 3 | 1 | 1 |
|
||||
| Phase 3: 开发 | 5 | 3 | 1 | 1 |
|
||||
| Phase 4: 测试 | 8 | 4 | 3 | 0 |
|
||||
| Phase 5: 部署 | 6 | 3 | 1 | 1 |
|
||||
| Phase 6: 运维 | 2 | 1 | 1 | 0 |
|
||||
| Phase 7: 升级 | 1 | 0 | 1 | 0 |
|
||||
|
||||
### DAG 依赖图
|
||||
```
|
||||
requirement_gathering → requirement_review
|
||||
→ table_design ──┬──→ design_review
|
||||
→ crud_design ──┤
|
||||
→ api_design ──┘
|
||||
design_review → code_generate → code_compliance_check
|
||||
→ code_auto_fix → code_review
|
||||
code_review → test_plan_create → test_case_generate
|
||||
→ functional_test ──┬──→ bug_report
|
||||
→ performance_test──┘
|
||||
bug_report → bug_fix → bug_verify → acceptance_test
|
||||
acceptance_test → deploy_env_collect → deploy_test
|
||||
→ deploy_test_verify → deploy_test_approve
|
||||
deploy_test_approve → deploy_production
|
||||
→ deploy_production_verify → monitor
|
||||
monitor → incident_response (if alerts)
|
||||
```
|
||||
|
||||
### 步骤间信息传递
|
||||
信息通过 artifact 传递 — handler 的 output_data 存入 `pipeline_artifacts` 表,
|
||||
下游通过 `input_data[dep_step_name]` 获取上游产物。
|
||||
|
||||
### 对话层信息流
|
||||
- 用户在驾驶舱输入框发送消息 → 存储到 sd_conversations → DSPY 处理
|
||||
- Agent 分析后可能在对话中回复,同时触发产线步骤流转
|
||||
- 关键步骤完成时 Agent 推送结果到对话面板
|
||||
|
||||
## 开发规范遵循
|
||||
|
||||
### 模块架构
|
||||
- 产线专属 UI(pipeline-sdlc)与引擎层(pipeline_task)严格分离
|
||||
- 跨模块调用:`entire_url('/pipeline_task/api/task_list.dspy')` 绝对路径
|
||||
- pipeline_task 零数据表,纯薄交互层
|
||||
|
||||
### UI 规范
|
||||
- index.ui 统一三层布局:Title (cheight:6) → 卡片区 (cheight:18) → VScrollPanel (css:filler)
|
||||
- 白底卡片 + SVG 图标(严禁暗色主题、emoji、backgroundColor)
|
||||
- Tabular popup → Tree 步骤树弹窗
|
||||
- toolbar 按钮 script 中用 `this` 取选中行
|
||||
|
||||
### DSPY 规范
|
||||
- 零 import(预加载:json、DBPools、get_user、params_kw、getID)
|
||||
- `return` 代替 `print`
|
||||
- `getID()` 代替 `uuid()`
|
||||
- `await get_user()` 不是 `get_user()`
|
||||
- 禁 `ServerEnv()`,用 `request._run_ns`
|
||||
- `sor.U()` 只要 2 个参数 (table, data_dict)
|
||||
- `await sor.sqlExe(sql, {})` 第二参数必须是 dict
|
||||
|
||||
### models/*.json 规范
|
||||
- summary[0].primary 必须是数组
|
||||
- float/double 字段必须有 len + dec
|
||||
- indexes idxfields 必须是数组
|
||||
- codes 引用 appcodes_kv 必须用 parentid= 不是 id=
|
||||
|
||||
### CRUD json/*.json 规范
|
||||
- 根键必须是 `tblname` + `params`
|
||||
- editable 必须有 new/update/delete_data_url
|
||||
- 使用 `{{entire_url('../api/xxx.dspy')}}` 格式
|
||||
|
||||
### 部署规范(铁律)
|
||||
- 本地改 → git commit → git push → 服务器 git pull → pip install(如需) → 重启
|
||||
- 禁直接在服务器改源文件
|
||||
- curl 200 ≠ 功能正常,必须浏览器验证
|
||||
179
docs/sdlc-cockpit-design.md
Normal file
179
docs/sdlc-cockpit-design.md
Normal file
@ -0,0 +1,179 @@
|
||||
# 开发产线 (SDLC Pipeline) 驾驶舱设计文档
|
||||
|
||||
## 设计理念
|
||||
|
||||
开发产线的用户界面采用**驾驶舱模式**设计。核心理念是:
|
||||
|
||||
- **像和大模型对话一样交互**:用户以自然语言描述需求,上传文件,Agent 自动分析和执行
|
||||
- **状态信息直线展示**:产线进度以时间线/步骤条形式一目了然
|
||||
- **详情按需展开**:点击步骤节点才展开具体内容(产物、日志、审批表单)
|
||||
|
||||
## 用户输入
|
||||
|
||||
统一为**类大模型交互界面**:
|
||||
|
||||
### 输入形式
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 📎 已附加: design-doc.pdf [×] │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ 请输入你的需求或回复... │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ [📎] [发送 ⏎]│
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 输入组件
|
||||
| 组件 | 说明 |
|
||||
|------|------|
|
||||
| 正文输入框 | 多行文本,支持自然语言描述需求、回复Agent |
|
||||
| 文件附件 | 支持上传多个文件(需求文档、设计稿、代码等) |
|
||||
| 发送按钮 | 提交后触发 Agent 分析并推入产线 |
|
||||
|
||||
### 输入可触发的操作
|
||||
- 新建迭代:描述需求 → Agent 自动创建迭代并启动产线
|
||||
- 回复人工任务:填写表单 → Agent 继续执行
|
||||
- 审批决策:approve / reject → 产线流转
|
||||
- 补充信息:代码修复说明、环境配置等
|
||||
|
||||
## 驾驶舱布局
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ 驾驶舱标题栏:[项目选择器▾] [迭代选择器▾] [刷新] │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ 统计卡片行:活跃项目 | 进行中迭代 | 待处理Bug | 待审批 │
|
||||
├───────────────────────────┬──────────────────────────────┤
|
||||
│ 时间线面板 (40%) │ 对话面板 (60%) │
|
||||
│ ┌────────────────────────┐│ ┌──────────────────────────┐ │
|
||||
│ │ 🟢 需求采集 ✓ ││ │ 🤖 Agent: 收到需求... │ │
|
||||
│ │ 🟢 需求评审 ✓ ││ │ 👤 你: 请增加XX功能 │ │
|
||||
│ │ 🟡 表设计 进行中 ││ │ 🤖 Agent: 设计完成, │ │
|
||||
│ │ ⚪ CRUD设计 待执行 ││ │ 请确认以下方案... │ │
|
||||
│ │ ⚪ API设计 待执行 ││ │ │ │
|
||||
│ │ ⚪ 设计评审 阻塞 ││ │ ┌──────────────────────┐ │ │
|
||||
│ │ ⚪ 代码生成 待执行 ││ │ │ 输入需求或回复... │ │ │
|
||||
│ └────────────────────────┘│ │ │ [📎][发送] │ │ │
|
||||
│ │ │ └──────────────────────┘ │ │
|
||||
│ [点击步骤展开详情→] │ └──────────────────────────┘ │
|
||||
└───────────────────────────┴──────────────────────────────┘
|
||||
```
|
||||
|
||||
## 时间线设计
|
||||
|
||||
### 步骤节点状态
|
||||
| 图标 | 状态 | 含义 |
|
||||
|------|------|------|
|
||||
| 🟢 ✓ | completed | 步骤已完成 |
|
||||
| 🟡 ⟳ | running | 正在执行中 |
|
||||
| ⚪ ○ | pending | 等待前置步骤完成 |
|
||||
| 🔵 ⚠ | waiting | 等待人工输入/审批 |
|
||||
| 🔴 ✗ | failed | 执行失败 |
|
||||
| ⏭ ⊘ | skipped | 已跳过 |
|
||||
| ⏸ ∥ | paused | 已暂停 |
|
||||
|
||||
### 时间线交互
|
||||
- **直线排列**:步骤按 DAG 拓扑排序后垂直排列,连接线显示依赖关系
|
||||
- **当前步骤高亮**:running/waiting 状态的步骤有脉冲动画
|
||||
- **点击展开**:点击任意步骤节点,在右侧面板或弹窗中展示详情
|
||||
- **详情内容**:
|
||||
- 步骤描述和配置
|
||||
- 输入/输出 artifact(支持 JSON 树形查看)
|
||||
- 执行日志
|
||||
- 如果是 human_task:显示表单或审批按钮
|
||||
- 如果是 failed:显示错误信息和重试按钮
|
||||
|
||||
### 步骤分组(时间段)
|
||||
```
|
||||
▼ 需求阶段
|
||||
🟢 需求采集
|
||||
🟢 需求评审
|
||||
▼ 设计阶段
|
||||
🟡 表设计
|
||||
⚪ CRUD设计
|
||||
⚪ API设计
|
||||
⚪ 设计评审
|
||||
▼ 开发阶段
|
||||
⚪ 代码生成
|
||||
...
|
||||
```
|
||||
|
||||
## 对话面板设计
|
||||
|
||||
### 消息类型
|
||||
| 角色 | 样式 | 说明 |
|
||||
|------|------|------|
|
||||
| 🤖 Agent | 左对齐,深色背景 | 系统自动消息:分析结果、步骤状态变更 |
|
||||
| 👤 用户 | 右对齐,蓝色背景 | 用户输入:需求、回复、审批 |
|
||||
| ⚡ 系统 | 居中,灰色小字 | 系统事件:步骤开始/完成、Bug上报 |
|
||||
|
||||
### Agent 消息内容
|
||||
- 需求分析结果
|
||||
- 设计产出预览(表结构、CRUD清单)
|
||||
- 代码审查报告
|
||||
- 测试结果摘要
|
||||
- 部署状态
|
||||
- **人工任务请求**:表单字段、审批选项
|
||||
|
||||
### 用户消息触发
|
||||
- 输入框发送 → 创建用户消息 → 调用 DSPY 处理 → Agent 回复
|
||||
|
||||
### 对话持久化
|
||||
消息存储在数据库表 `sd_conversations` 中,与迭代关联。
|
||||
|
||||
## 数据模型
|
||||
|
||||
### sd_conversations(对话消息表)
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | str(32) | 主键 |
|
||||
| iteration_id | str(32) | 关联迭代 |
|
||||
| task_id | str(32) | 关联 Pipeline 任务 |
|
||||
| step_name | str(100) | 关联步骤(可为空) |
|
||||
| role | str(20) | agent / user / system |
|
||||
| content | text | 消息正文 |
|
||||
| attachments | text | 附件列表 JSON |
|
||||
| created_at | timestamp | 创建时间 |
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 页面路由
|
||||
- `/sd_cockpit/` → 驾驶舱主页面
|
||||
- `/sd_cockpit/timeline.dspy` → 时间线数据 API
|
||||
- `/sd_cockpit/chat_messages.dspy` → 对话消息 API(GET 读取 / POST 发送)
|
||||
- `/sd_cockpit/step_detail.dspy` → 步骤详情 API
|
||||
- `/sd_cockpit/dashboard_data.dspy` → 统计卡片数据
|
||||
|
||||
### Bricks Widget 映射
|
||||
| 设计元素 | Bricks 实现 |
|
||||
|----------|------------|
|
||||
| 时间线列表 | VBox + 自定义 styled VBox items |
|
||||
| 对话面板 | VScrollPanel + VBox message bubbles |
|
||||
| 输入框 | Form widget (textarea + submit) |
|
||||
| 统计卡片 | ResponsableBox + VBox.card |
|
||||
| 步骤详情 | Popup + urlwidget (加载 step_detail.ui) |
|
||||
| 文件上传 | Form file field |
|
||||
| 项目/迭代选择 | code dropdown widget |
|
||||
|
||||
### 跨模块调用
|
||||
驾驶舱在 pipeline-sdlc 模块内,调用 pipeline_task API 时使用绝对路径:
|
||||
```
|
||||
entire_url('/pipeline_task/api/task_submit.dspy')
|
||||
entire_url('/pipeline_task/api/task_detail.dspy')
|
||||
entire_url('/pipeline_task/api/human_complete.dspy')
|
||||
```
|
||||
|
||||
## 开发规范遵循
|
||||
|
||||
| 规范项 | 应用 |
|
||||
|--------|------|
|
||||
| 布局标准 | 四卡片 + filler 内容区,白底 SVG 图标 |
|
||||
| DSPY 规范 | 零 import、return 代替 print、getID()、await get_user() |
|
||||
| 跨模块调用 | 绝对路径 entire_url('/pipeline_task/api/...') |
|
||||
| 部署铁律 | 本地改 → commit → push → 服务器 pull → 重启 → 浏览器验证 |
|
||||
| DSPY async | 用 request._run_ns,不用 ServerEnv() |
|
||||
| sor.U() | 只传2个参数 |
|
||||
| 禁止 | 不直接改服务器原文,不动其他模块 |
|
||||
31
models/sd_conversations.json
Normal file
31
models/sd_conversations.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "sd_conversations",
|
||||
"title": "开发产线对话记录表",
|
||||
"primary": ["id"],
|
||||
"catelog": "entity"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "iteration_id", "title": "迭代ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "task_id", "title": "Pipeline任务ID", "type": "str", "length": 32},
|
||||
{"name": "step_name", "title": "关联步骤名", "type": "str", "length": 100},
|
||||
{"name": "role", "title": "角色", "type": "str", "length": 20, "nullable": "no", "default": "'user'"},
|
||||
{"name": "content", "title": "消息正文", "type": "text"},
|
||||
{"name": "attachments", "title": "附件列表JSON", "type": "text"},
|
||||
{"name": "msg_type", "title": "消息类型", "type": "str", "length": 20, "nullable": "no", "default": "'text'"},
|
||||
{"name": "org_id", "title": "组织ID", "type": "str", "length": 32, "nullable": "no", "default": "'0'"},
|
||||
{"name": "created_by", "title": "创建人", "type": "str", "length": 32},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_sd_conv_iteration", "idxtype": "index", "idxfields": ["iteration_id"]},
|
||||
{"name": "idx_sd_conv_task", "idxtype": "index", "idxfields": ["task_id"]},
|
||||
{"name": "idx_sd_conv_created", "idxtype": "index", "idxfields": ["created_at"]}
|
||||
],
|
||||
"codes": [
|
||||
{"field": "role", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='sd_conversation_role'"}
|
||||
]
|
||||
}
|
||||
146
wwwroot/api/cockpit_chat.dspy
Normal file
146
wwwroot/api/cockpit_chat.dspy
Normal file
@ -0,0 +1,146 @@
|
||||
# cockpit_chat.dspy - Send message or list conversation
|
||||
# POST: action=send_message, iteration_id, message_text
|
||||
# GET: action=list_messages, iteration_id (optional), task_id (optional)
|
||||
|
||||
action = (params_kw or {}).get('action', 'list_messages')
|
||||
dbname = get_module_dbname('pipeline-sdlc')
|
||||
|
||||
if action == 'send_message':
|
||||
iteration_id = (params_kw or {}).get('iteration_id', '')
|
||||
task_id = (params_kw or {}).get('task_id', '')
|
||||
message_text = (params_kw or {}).get('message_text', '').strip()
|
||||
|
||||
if not iteration_id or not message_text:
|
||||
return json.dumps({"error": "Missing iteration_id or message_text"}, ensure_ascii=False)
|
||||
|
||||
uid = await get_user()
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
# Save user message
|
||||
msg_id = getID()
|
||||
await sor.I('sd_conversations', {
|
||||
'id': msg_id,
|
||||
'iteration_id': iteration_id,
|
||||
'task_id': task_id or '',
|
||||
'step_name': '',
|
||||
'role': 'user',
|
||||
'content': message_text,
|
||||
'attachments': '[]',
|
||||
'msg_type': 'text',
|
||||
'org_id': '0',
|
||||
'created_by': uid
|
||||
})
|
||||
|
||||
# Generate agent response
|
||||
# 1. Check if there's an active pipeline task for this iteration
|
||||
if task_id:
|
||||
agent_reply = f"收到你的消息。当前任务 {task_id} 正在执行中,你的输入已记录。"
|
||||
else:
|
||||
# Check for existing iterations
|
||||
iters = await sor.sqlExe(
|
||||
"SELECT id, task_id, status FROM sd_iterations WHERE id=${iid}$", {"iid": iteration_id})
|
||||
if iters:
|
||||
it = iters[0]
|
||||
if hasattr(it, 'task_id') and it.task_id:
|
||||
agent_reply = f"收到。迭代任务 {it.task_id} 状态: {it.status}。"
|
||||
else:
|
||||
agent_reply = f"收到你的需求:「{message_text[:50]}...」。请确认是否要为此迭代启动开发产线?"
|
||||
else:
|
||||
agent_reply = f"收到你的需求:「{message_text[:50]}...」。建议先在项目管理中创建项目和迭代。"
|
||||
|
||||
# Save agent message
|
||||
agent_msg_id = getID()
|
||||
await sor.I('sd_conversations', {
|
||||
'id': agent_msg_id,
|
||||
'iteration_id': iteration_id,
|
||||
'task_id': task_id or '',
|
||||
'step_name': '',
|
||||
'role': 'agent',
|
||||
'content': agent_reply,
|
||||
'attachments': '[]',
|
||||
'msg_type': 'text',
|
||||
'org_id': '0',
|
||||
'created_by': 'system'
|
||||
})
|
||||
|
||||
return json.dumps({"success": True, "message_id": msg_id}, ensure_ascii=False)
|
||||
|
||||
else:
|
||||
# list_messages - return messages as Bricks widget JSON
|
||||
iteration_id = (params_kw or {}).get('iteration_id', '')
|
||||
task_id = (params_kw or {}).get('task_id', '')
|
||||
|
||||
msgs = []
|
||||
if iteration_id or task_id:
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
where = []
|
||||
params = {}
|
||||
if task_id:
|
||||
where.append("task_id=${tid}$")
|
||||
params["tid"] = task_id
|
||||
if iteration_id:
|
||||
where.append("iteration_id=${iid}$")
|
||||
params["iid"] = iteration_id
|
||||
|
||||
sql = f"SELECT role, content, msg_type, created_at FROM sd_conversations WHERE {' OR '.join(where)} ORDER BY created_at ASC LIMIT 50"
|
||||
msgs = await sor.sqlExe(sql, params)
|
||||
|
||||
# Build chat messages widget
|
||||
msg_widgets = []
|
||||
for m in msgs:
|
||||
role = m.role if hasattr(m, 'role') else ''
|
||||
content = m.content if hasattr(m, 'content') else ''
|
||||
|
||||
if role == 'agent':
|
||||
bg = '#e8f0fe'
|
||||
align = 'flex-start'
|
||||
label = 'Agent'
|
||||
label_color = '#3b82f6'
|
||||
elif role == 'user':
|
||||
bg = '#dbeafe'
|
||||
align = 'flex-end'
|
||||
label = '你'
|
||||
label_color = '#2563eb'
|
||||
else:
|
||||
bg = '#f1f5f9'
|
||||
align = 'center'
|
||||
label = '系统'
|
||||
label_color = '#94a3b8'
|
||||
|
||||
msg_widgets.append({
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "85%",
|
||||
"alignSelf": align,
|
||||
"bgcolor": bg,
|
||||
"borderRadius": "12px",
|
||||
"padding": "12px 16px",
|
||||
"marginBottom": "10px",
|
||||
"gap": "4px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": label, "cfontsize": 0.75,
|
||||
"color": label_color, "fontWeight": "bold"
|
||||
}},
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": content, "cfontsize": 0.95,
|
||||
"color": "#1e293b", "whiteSpace": "pre-wrap"
|
||||
}}
|
||||
]
|
||||
})
|
||||
|
||||
if not msg_widgets:
|
||||
msg_widgets.append({
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "暂无对话记录。选择一个迭代后,在下方输入框中开始对话。",
|
||||
"cfontsize": 0.9, "color": "#94a3b8", "padding": "20px"
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "padding": "4px"},
|
||||
"subwidgets": msg_widgets
|
||||
}
|
||||
123
wwwroot/api/cockpit_stats.dspy
Normal file
123
wwwroot/api/cockpit_stats.dspy
Normal file
@ -0,0 +1,123 @@
|
||||
# cockpit_stats.dspy - Returns dashboard stat cards as Bricks widget JSON
|
||||
dbname = get_module_dbname('pipeline-sdlc')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
# Active projects
|
||||
proj = await sor.sqlExe(
|
||||
"SELECT COUNT(*) as active FROM sd_projects WHERE status='active'", {})
|
||||
active_projects = proj[0].active if proj else 0
|
||||
|
||||
# Active iterations
|
||||
iters = await sor.sqlExe(
|
||||
"SELECT COUNT(*) as cnt FROM sd_iterations WHERE status='in_progress'", {})
|
||||
active_iters = iters[0].cnt if iters else 0
|
||||
|
||||
# Open bugs
|
||||
bugs = await sor.sqlExe(
|
||||
"SELECT COUNT(*) as cnt FROM sd_bugs WHERE status NOT IN ('closed','resolved')", {})
|
||||
open_bugs = bugs[0].cnt if bugs else 0
|
||||
|
||||
# Pending reviews (human tasks)
|
||||
reviews = await sor.sqlExe(
|
||||
"SELECT COUNT(*) as cnt FROM pipeline_human_tasks WHERE status='pending'", {})
|
||||
pending_reviews = reviews[0].cnt if reviews else 0
|
||||
|
||||
return {
|
||||
"widgettype": "ResponsableBox",
|
||||
"options": {"gap": "16px", "minWidth": "180px", "width": "100%"},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card", "cwidth": 23, "cheight": 12,
|
||||
"padding": "20px", "bgcolor": "#fff",
|
||||
"borderRadius": "8px", "border": "1px solid #e2e8f0",
|
||||
"alignItems": "center", "gap": "6px", "cursor": "pointer"
|
||||
},
|
||||
"binds": [{
|
||||
"wid": "self", "event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.main_content",
|
||||
"options": {"url": entire_url("/pipeline-sdlc/sd_projects/")},
|
||||
"mode": "replace"
|
||||
}],
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": f"{active_projects}", "cfontsize": 2.2,
|
||||
"color": "#3b82f6", "fontWeight": "bold"
|
||||
}},
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": "活跃项目", "cfontsize": 0.9, "color": "#64748b"
|
||||
}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card", "cwidth": 23, "cheight": 12,
|
||||
"padding": "20px", "bgcolor": "#fff",
|
||||
"borderRadius": "8px", "border": "1px solid #e2e8f0",
|
||||
"alignItems": "center", "gap": "6px", "cursor": "pointer"
|
||||
},
|
||||
"binds": [{
|
||||
"wid": "self", "event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.main_content",
|
||||
"options": {"url": entire_url("/pipeline-sdlc/sd_iterations/")},
|
||||
"mode": "replace"
|
||||
}],
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": f"{active_iters}", "cfontsize": 2.2,
|
||||
"color": "#22c55e", "fontWeight": "bold"
|
||||
}},
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": "进行中迭代", "cfontsize": 0.9, "color": "#64748b"
|
||||
}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card", "cwidth": 23, "cheight": 12,
|
||||
"padding": "20px", "bgcolor": "#fff",
|
||||
"borderRadius": "8px", "border": "1px solid #e2e8f0",
|
||||
"alignItems": "center", "gap": "6px", "cursor": "pointer"
|
||||
},
|
||||
"binds": [{
|
||||
"wid": "self", "event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.main_content",
|
||||
"options": {"url": entire_url("/pipeline-sdlc/sd_bugs/")},
|
||||
"mode": "replace"
|
||||
}],
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": f"{open_bugs}", "cfontsize": 2.2,
|
||||
"color": "#f59e0b", "fontWeight": "bold"
|
||||
}},
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": "待处理Bug", "cfontsize": 0.9, "color": "#64748b"
|
||||
}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card", "cwidth": 23, "cheight": 12,
|
||||
"padding": "20px", "bgcolor": "#fff",
|
||||
"borderRadius": "8px", "border": "1px solid #e2e8f0",
|
||||
"alignItems": "center", "gap": "6px", "cursor": "pointer"
|
||||
},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": f"{pending_reviews}", "cfontsize": 2.2,
|
||||
"color": "#8b5cf6", "fontWeight": "bold"
|
||||
}},
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": "待审批", "cfontsize": 0.9, "color": "#64748b"
|
||||
}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
159
wwwroot/api/cockpit_timeline.dspy
Normal file
159
wwwroot/api/cockpit_timeline.dspy
Normal file
@ -0,0 +1,159 @@
|
||||
# cockpit_timeline.dspy - Returns pipeline step timeline as Bricks widget JSON
|
||||
# params: iteration_id (optional), task_id (optional)
|
||||
|
||||
iteration_id = (params_kw or {}).get('iteration_id', '')
|
||||
task_id = (params_kw or {}).get('task_id', '')
|
||||
dbname = get_module_dbname('pipeline-sdlc')
|
||||
|
||||
if not task_id and iteration_id:
|
||||
# Look up task_id from iteration
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
iters = await sor.sqlExe(
|
||||
"SELECT task_id FROM sd_iterations WHERE id=${iid}$", {"iid": iteration_id})
|
||||
if iters and hasattr(iters[0], 'task_id') and iters[0].task_id:
|
||||
task_id = iters[0].task_id
|
||||
|
||||
if not task_id:
|
||||
return {
|
||||
"widgettype": "VBox",
|
||||
"options": {"padding": "20px", "alignItems": "center"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": "暂无产线任务。\n请先创建迭代以启动开发产线。",
|
||||
"cfontsize": 0.9, "color": "#94a3b8", "textAlign": "center"
|
||||
}}
|
||||
]
|
||||
}
|
||||
|
||||
# Query pipeline steps via pipeline_service
|
||||
try:
|
||||
from pipeline_service.executor import get_task_steps
|
||||
steps = await get_task_steps(task_id)
|
||||
except Exception:
|
||||
# Fallback: query pipeline_tasks and pipeline_task_steps directly
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
steps = await sor.sqlExe(
|
||||
"SELECT step_name, state, step_order, step_config, started_at, completed_at "
|
||||
"FROM pipeline_task_steps WHERE task_id=${tid}$ ORDER BY step_order",
|
||||
{"tid": task_id}
|
||||
)
|
||||
|
||||
# Status icon mapping
|
||||
status_config = {
|
||||
'completed': {'icon': '✓', 'color': '#22c55e', 'bg': '#f0fdf4', 'label': '已完成'},
|
||||
'running': {'icon': '⟳', 'color': '#f59e0b', 'bg': '#fffbeb', 'label': '执行中'},
|
||||
'pending': {'icon': '○', 'color': '#94a3b8', 'bg': '#f8fafc', 'label': '待执行'},
|
||||
'waiting': {'icon': '⏳', 'color': '#3b82f6', 'bg': '#eff6ff', 'label': '等待中'},
|
||||
'failed': {'icon': '✗', 'color': '#ef4444', 'bg': '#fef2f2', 'label': '失败'},
|
||||
'paused': {'icon': '⏸', 'color': '#8b5cf6', 'bg': '#f5f3ff', 'label': '已暂停'},
|
||||
'skipped': {'icon': '⊘', 'color': '#94a3b8', 'bg': '#f1f5f9', 'label': '已跳过'},
|
||||
}
|
||||
|
||||
step_widgets = []
|
||||
|
||||
# Group by phase
|
||||
current_phase = None
|
||||
for step in (steps or []):
|
||||
sn = step.step_name if hasattr(step, 'step_name') else step.get('step_name', '')
|
||||
state = step.state if hasattr(step, 'state') else step.get('state', 'pending')
|
||||
step_config_raw = step.step_config if hasattr(step, 'step_config') else step.get('step_config', '{}')
|
||||
step_order = step.step_order if hasattr(step, 'step_order') else step.get('step_order', 0)
|
||||
|
||||
# Parse step_config for phase grouping
|
||||
try:
|
||||
cfg = json.loads(step_config_raw) if isinstance(step_config_raw, str) else (step_config_raw or {})
|
||||
except Exception:
|
||||
cfg = {}
|
||||
phase = cfg.get('phase', '')
|
||||
phase_label = cfg.get('phase_label', '')
|
||||
|
||||
# Phase header
|
||||
if phase and phase != current_phase:
|
||||
current_phase = phase
|
||||
step_widgets.append({
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"width": "100%", "alignItems": "center",
|
||||
"padding": "12px 0 6px 0", "gap": "8px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Svg", "options": {
|
||||
"svg": '<svg width="16" height="16" viewBox="0 0 16 16"><polygon points="8,0 16,8 8,16 0,8" fill="#cbd5e1"/></svg>',
|
||||
"width": "12px", "height": "12px"
|
||||
}},
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": phase_label or phase, "cfontsize": 0.85,
|
||||
"color": "#64748b", "fontWeight": "bold"
|
||||
}}
|
||||
]
|
||||
})
|
||||
|
||||
# Status
|
||||
sc = status_config.get(state, status_config['pending'])
|
||||
# Connection line
|
||||
connector = {
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "2px", "height": "100%", "minHeight": "8px",
|
||||
"bgcolor": "#e2e8f0", "marginLeft": "9px"}
|
||||
}
|
||||
|
||||
step_widgets.append({
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"width": "100%", "alignItems": "flex-start",
|
||||
"padding": "4px 0", "gap": "10px", "cursor": "pointer"
|
||||
},
|
||||
"binds": [{
|
||||
"wid": "self", "event": "click",
|
||||
"actiontype": "popup",
|
||||
"popup_desc": {
|
||||
"widgettype": "urlwidget",
|
||||
"options": {
|
||||
"url": entire_url(f"/pipeline_task/step_panel.ui?task_id=" + task_id + "&step_name=" + sn)
|
||||
}
|
||||
},
|
||||
"popupwindow": True,
|
||||
"options": {"title": f"步骤详情: {sn}", "width": "700px", "height": "500px"}
|
||||
}],
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"alignItems": "center", "minWidth": "24px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": sc['icon'], "cfontsize": 1.2,
|
||||
"color": sc['color'], "fontWeight": "bold"
|
||||
}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"flex": 1, "gap": "2px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": sn, "cfontsize": 0.9,
|
||||
"color": "#1e293b", "fontWeight": "500"
|
||||
}},
|
||||
{"widgettype": "Text", "options": {
|
||||
"text": sc['label'], "cfontsize": 0.75,
|
||||
"color": sc['color']
|
||||
}}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
if not step_widgets:
|
||||
step_widgets.append({
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "产线步骤尚未生成。\n任务初始化中...",
|
||||
"cfontsize": 0.9, "color": "#94a3b8", "padding": "20px"
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "padding": "4px"},
|
||||
"subwidgets": step_widgets
|
||||
}
|
||||
586
wwwroot/index.ui
586
wwwroot/index.ui
@ -1,103 +1,495 @@
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "height": "100%", "padding": "0"},
|
||||
"subwidgets": [
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"padding": "0"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"alignItems": "center",
|
||||
"padding": "24px 24px 12px 24px",
|
||||
"cheight": 6
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {"width": "100%", "alignItems": "center", "padding": "24px 24px 12px 24px", "cheight": 6},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Title2", "options": {"text": "开发产线"}},
|
||||
{"widgettype": "Filler"},
|
||||
{"widgettype": "Text", "options": {"text": "项目管理、迭代跟踪、测试与部署", "cfontsize": 1.2}}
|
||||
]
|
||||
"widgettype": "Title2",
|
||||
"options": {
|
||||
"text": "开发产线"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {"width": "100%", "padding": "12px 24px 12px 24px", "cheight": 18},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "ResponsableBox",
|
||||
"options": {"gap": "24px", "minWidth": "260px"},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"css": "card", "cwidth": 23, "cheight": 12, "padding": "24px", "cursor": "pointer", "bgcolor": "#fff"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.pipeline_sdlc_content", "options": {"url": "{{entire_url('sd_projects/')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [
|
||||
{"widgettype": "VBox", "options": {"width": "44px", "height": "44px", "bgcolor": "#EFF6FF", "borderRadius": "12px", "alignItems": "center", "justifyContent": "center", "marginBottom": "12px"}, "subwidgets": [
|
||||
{"widgettype": "Svg", "options": {"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#3B82F6\" stroke-width=\"2\"><path d=\"M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z\"/></svg>", "width": "24px", "height": "24px"}}
|
||||
]},
|
||||
{"widgettype": "Title3", "options": {"text": "项目管理", "marginBottom": "4px"}},
|
||||
{"widgettype": "Text", "options": {"text": "创建与管理软件项目", "cfontsize": 1.2}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"css": "card", "cwidth": 23, "cheight": 12, "padding": "24px", "cursor": "pointer", "bgcolor": "#fff"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.pipeline_sdlc_content", "options": {"url": "{{entire_url('sd_iterations/')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [
|
||||
{"widgettype": "VBox", "options": {"width": "44px", "height": "44px", "bgcolor": "#F0FDF4", "borderRadius": "12px", "alignItems": "center", "justifyContent": "center", "marginBottom": "12px"}, "subwidgets": [
|
||||
{"widgettype": "Svg", "options": {"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#22C55E\" stroke-width=\"2\"><polyline points=\"23 4 23 10 17 10\"/><path d=\"M20.49 15a9 9 0 11-2.12-9.36L23 10\"/></svg>", "width": "24px", "height": "24px"}}
|
||||
]},
|
||||
{"widgettype": "Title3", "options": {"text": "迭代管理", "marginBottom": "4px"}},
|
||||
{"widgettype": "Text", "options": {"text": "规划Sprint与迭代周期", "cfontsize": 1.2}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"css": "card", "cwidth": 23, "cheight": 12, "padding": "24px", "cursor": "pointer", "bgcolor": "#fff"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.pipeline_sdlc_content", "options": {"url": "{{entire_url('sd_test_plans/')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [
|
||||
{"widgettype": "VBox", "options": {"width": "44px", "height": "44px", "bgcolor": "#F5F3FF", "borderRadius": "12px", "alignItems": "center", "justifyContent": "center", "marginBottom": "12px"}, "subwidgets": [
|
||||
{"widgettype": "Svg", "options": {"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#8B5CF6\" stroke-width=\"2\"><path d=\"M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2\"/><rect x=\"8\" y=\"2\" width=\"8\" height=\"4\" rx=\"1\"/></svg>", "width": "24px", "height": "24px"}}
|
||||
]},
|
||||
{"widgettype": "Title3", "options": {"text": "测试计划", "marginBottom": "4px"}},
|
||||
{"widgettype": "Text", "options": {"text": "制定与跟踪测试计划", "cfontsize": 1.2}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"css": "card", "cwidth": 23, "cheight": 12, "padding": "24px", "cursor": "pointer", "bgcolor": "#fff"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.pipeline_sdlc_content", "options": {"url": "{{entire_url('sd_test_cases/')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [
|
||||
{"widgettype": "VBox", "options": {"width": "44px", "height": "44px", "bgcolor": "#FFF7ED", "borderRadius": "12px", "alignItems": "center", "justifyContent": "center", "marginBottom": "12px"}, "subwidgets": [
|
||||
{"widgettype": "Svg", "options": {"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#F97316\" stroke-width=\"2\"><path d=\"M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/></svg>", "width": "24px", "height": "24px"}}
|
||||
]},
|
||||
{"widgettype": "Title3", "options": {"text": "测试用例", "marginBottom": "4px"}},
|
||||
{"widgettype": "Text", "options": {"text": "编写与管理测试用例", "cfontsize": 1.2}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"css": "card", "cwidth": 23, "cheight": 12, "padding": "24px", "cursor": "pointer", "bgcolor": "#fff"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.pipeline_sdlc_content", "options": {"url": "{{entire_url('sd_bugs/')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [
|
||||
{"widgettype": "VBox", "options": {"width": "44px", "height": "44px", "bgcolor": "#FEF2F2", "borderRadius": "12px", "alignItems": "center", "justifyContent": "center", "marginBottom": "12px"}, "subwidgets": [
|
||||
{"widgettype": "Svg", "options": {"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#EF4444\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>", "width": "24px", "height": "24px"}}
|
||||
]},
|
||||
{"widgettype": "Title3", "options": {"text": "Bug管理", "marginBottom": "4px"}},
|
||||
{"widgettype": "Text", "options": {"text": "跟踪与修复项目缺陷", "cfontsize": 1.2}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"css": "card", "cwidth": 23, "cheight": 12, "padding": "24px", "cursor": "pointer", "bgcolor": "#fff"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.pipeline_sdlc_content", "options": {"url": "{{entire_url('sd_deploy_envs/')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [
|
||||
{"widgettype": "VBox", "options": {"width": "44px", "height": "44px", "bgcolor": "#F0FDFA", "borderRadius": "12px", "alignItems": "center", "justifyContent": "center", "marginBottom": "12px"}, "subwidgets": [
|
||||
{"widgettype": "Svg", "options": {"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#14B8A6\" stroke-width=\"2\"><rect x=\"2\" y=\"2\" width=\"20\" height=\"8\" rx=\"2\"/><rect x=\"2\" y=\"14\" width=\"20\" height=\"8\" rx=\"2\"/><line x1=\"6\" y1=\"6\" x2=\"6.01\" y2=\"6\"/><line x1=\"6\" y1=\"18\" x2=\"6.01\" y2=\"18\"/></svg>", "width": "24px", "height": "24px"}}
|
||||
]},
|
||||
{"widgettype": "Title3", "options": {"text": "部署环境", "marginBottom": "4px"}},
|
||||
{"widgettype": "Text", "options": {"text": "配置开发测试生产环境", "cfontsize": 1.2}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]},
|
||||
"widgettype": "Filler"
|
||||
},
|
||||
{
|
||||
"widgettype": "VScrollPanel",
|
||||
"id": "pipeline_sdlc_content",
|
||||
"options": {"css": "filler", "width": "100%", "height": "100%"}
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "项目管理、迭代跟踪、测试与部署",
|
||||
"cfontsize": 1.2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"padding": "12px 24px 12px 24px",
|
||||
"cheight": 18
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "ResponsableBox",
|
||||
"options": {
|
||||
"gap": "24px",
|
||||
"minWidth": "260px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card",
|
||||
"cwidth": 23,
|
||||
"cheight": 12,
|
||||
"padding": "24px",
|
||||
"cursor": "pointer",
|
||||
"bgcolor": "#1e293b",
|
||||
"border": "1px solid #6366f1",
|
||||
"borderRadius": "12px"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.main_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('/pipeline-sdlc/sd_cockpit/')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
],
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "44px",
|
||||
"height": "44px",
|
||||
"bgcolor": "#312e81",
|
||||
"borderRadius": "12px",
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Svg",
|
||||
"options": {
|
||||
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#818cf8\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 6v6l4 2\"/></svg>",
|
||||
"width": "24px",
|
||||
"height": "24px"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "Title3",
|
||||
"options": {
|
||||
"text": "驾驶舱",
|
||||
"marginBottom": "4px"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "AI驱动的对话式开发",
|
||||
"cfontsize": 0.9,
|
||||
"color": "#94a3b8"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card",
|
||||
"cwidth": 23,
|
||||
"cheight": 12,
|
||||
"padding": "24px",
|
||||
"cursor": "pointer",
|
||||
"bgcolor": "#fff"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.main_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('/pipeline-sdlc/sd_projects/')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
],
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "44px",
|
||||
"height": "44px",
|
||||
"bgcolor": "#EFF6FF",
|
||||
"borderRadius": "12px",
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Svg",
|
||||
"options": {
|
||||
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#3B82F6\" stroke-width=\"2\"><path d=\"M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z\"/></svg>",
|
||||
"width": "24px",
|
||||
"height": "24px"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "Title3",
|
||||
"options": {
|
||||
"text": "项目管理",
|
||||
"marginBottom": "4px"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "创建与管理软件项目",
|
||||
"cfontsize": 1.2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card",
|
||||
"cwidth": 23,
|
||||
"cheight": 12,
|
||||
"padding": "24px",
|
||||
"cursor": "pointer",
|
||||
"bgcolor": "#fff"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.main_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('/pipeline-sdlc/sd_iterations/')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
],
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "44px",
|
||||
"height": "44px",
|
||||
"bgcolor": "#F0FDF4",
|
||||
"borderRadius": "12px",
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Svg",
|
||||
"options": {
|
||||
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#22C55E\" stroke-width=\"2\"><polyline points=\"23 4 23 10 17 10\"/><path d=\"M20.49 15a9 9 0 11-2.12-9.36L23 10\"/></svg>",
|
||||
"width": "24px",
|
||||
"height": "24px"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "Title3",
|
||||
"options": {
|
||||
"text": "迭代管理",
|
||||
"marginBottom": "4px"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "规划Sprint与迭代周期",
|
||||
"cfontsize": 1.2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card",
|
||||
"cwidth": 23,
|
||||
"cheight": 12,
|
||||
"padding": "24px",
|
||||
"cursor": "pointer",
|
||||
"bgcolor": "#fff"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.main_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('/pipeline-sdlc/sd_test_plans/')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
],
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "44px",
|
||||
"height": "44px",
|
||||
"bgcolor": "#F5F3FF",
|
||||
"borderRadius": "12px",
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Svg",
|
||||
"options": {
|
||||
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#8B5CF6\" stroke-width=\"2\"><path d=\"M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2\"/><rect x=\"8\" y=\"2\" width=\"8\" height=\"4\" rx=\"1\"/></svg>",
|
||||
"width": "24px",
|
||||
"height": "24px"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "Title3",
|
||||
"options": {
|
||||
"text": "测试计划",
|
||||
"marginBottom": "4px"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "制定与跟踪测试计划",
|
||||
"cfontsize": 1.2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card",
|
||||
"cwidth": 23,
|
||||
"cheight": 12,
|
||||
"padding": "24px",
|
||||
"cursor": "pointer",
|
||||
"bgcolor": "#fff"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.main_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('/pipeline-sdlc/sd_test_cases/')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
],
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "44px",
|
||||
"height": "44px",
|
||||
"bgcolor": "#FFF7ED",
|
||||
"borderRadius": "12px",
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Svg",
|
||||
"options": {
|
||||
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#F97316\" stroke-width=\"2\"><path d=\"M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/></svg>",
|
||||
"width": "24px",
|
||||
"height": "24px"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "Title3",
|
||||
"options": {
|
||||
"text": "测试用例",
|
||||
"marginBottom": "4px"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "编写与管理测试用例",
|
||||
"cfontsize": 1.2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card",
|
||||
"cwidth": 23,
|
||||
"cheight": 12,
|
||||
"padding": "24px",
|
||||
"cursor": "pointer",
|
||||
"bgcolor": "#fff"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.main_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('/pipeline-sdlc/sd_bugs/')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
],
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "44px",
|
||||
"height": "44px",
|
||||
"bgcolor": "#FEF2F2",
|
||||
"borderRadius": "12px",
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Svg",
|
||||
"options": {
|
||||
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#EF4444\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>",
|
||||
"width": "24px",
|
||||
"height": "24px"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "Title3",
|
||||
"options": {
|
||||
"text": "Bug管理",
|
||||
"marginBottom": "4px"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "跟踪与修复项目缺陷",
|
||||
"cfontsize": 1.2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card",
|
||||
"cwidth": 23,
|
||||
"cheight": 12,
|
||||
"padding": "24px",
|
||||
"cursor": "pointer",
|
||||
"bgcolor": "#fff"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.main_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('/pipeline-sdlc/sd_deploy_envs/')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
],
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "44px",
|
||||
"height": "44px",
|
||||
"bgcolor": "#F0FDFA",
|
||||
"borderRadius": "12px",
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Svg",
|
||||
"options": {
|
||||
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#14B8A6\" stroke-width=\"2\"><rect x=\"2\" y=\"2\" width=\"20\" height=\"8\" rx=\"2\"/><rect x=\"2\" y=\"14\" width=\"20\" height=\"8\" rx=\"2\"/><line x1=\"6\" y1=\"6\" x2=\"6.01\" y2=\"6\"/><line x1=\"6\" y1=\"18\" x2=\"6.01\" y2=\"18\"/></svg>",
|
||||
"width": "24px",
|
||||
"height": "24px"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "Title3",
|
||||
"options": {
|
||||
"text": "部署环境",
|
||||
"marginBottom": "4px"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "配置开发测试生产环境",
|
||||
"cfontsize": 1.2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VScrollPanel",
|
||||
"id": "pipeline_sdlc_content",
|
||||
"options": {
|
||||
"css": "filler",
|
||||
"width": "100%",
|
||||
"height": "100%"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
257
wwwroot/sd_cockpit/index.ui
Normal file
257
wwwroot/sd_cockpit/index.ui
Normal file
@ -0,0 +1,257 @@
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"padding": "0"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"alignItems": "center",
|
||||
"padding": "20px 24px 12px 24px",
|
||||
"cheight": 6,
|
||||
"gap": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Title2",
|
||||
"options": {
|
||||
"text": "开发产线驾驶舱"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "Cockpit",
|
||||
"cfontsize": 0.9,
|
||||
"color": "#94a3b8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Filler"
|
||||
},
|
||||
{
|
||||
"widgettype": "Button",
|
||||
"options": {
|
||||
"name": "refresh_btn",
|
||||
"label": "刷新",
|
||||
"css": "small"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "click",
|
||||
"actiontype": "script",
|
||||
"target": "self",
|
||||
"script": "var p=bricks.getWidgetById('cockpit_timeline');if(p)p.render({});var c=bricks.getWidgetById('cockpit_chat');if(c)c.render({});var d=bricks.getWidgetById('cockpit_dashboard');if(d)d.render({});"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"padding": "12px 24px 12px 24px",
|
||||
"cheight": 18,
|
||||
"gap": "16px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "urlwidget",
|
||||
"id": "cockpit_dashboard",
|
||||
"options": {
|
||||
"url": "{{entire_url('/pipeline-sdlc/api/cockpit_stats.dspy')}}",
|
||||
"method": "GET"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"css": "filler",
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"padding": "12px 24px 24px 24px",
|
||||
"gap": "16px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"cwidth": 35,
|
||||
"height": "100%",
|
||||
"gap": "8px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"alignItems": "center",
|
||||
"padding": "0 0 8px 0",
|
||||
"gap": "8px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Title3",
|
||||
"options": {
|
||||
"text": "产线时间线"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "选择一个迭代查看",
|
||||
"cfontsize": 0.8,
|
||||
"color": "#94a3b8"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VScrollPanel",
|
||||
"id": "cockpit_timeline",
|
||||
"options": {
|
||||
"css": "filler",
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"bgcolor": "#f8fafc",
|
||||
"border": "1px solid #e2e8f0",
|
||||
"borderRadius": "8px",
|
||||
"padding": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "请先在项目管理中创建迭代,或从工具栏选择一个迭代来查看产线进度。",
|
||||
"cfontsize": 0.9,
|
||||
"color": "#64748b",
|
||||
"padding": "20px"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"cwidth": 65,
|
||||
"height": "100%",
|
||||
"gap": "8px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"alignItems": "center",
|
||||
"padding": "0 0 8px 0",
|
||||
"gap": "8px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Title3",
|
||||
"options": {
|
||||
"text": "对话"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Filler"
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "像和大模型对话一样驱动开发",
|
||||
"cfontsize": 0.8,
|
||||
"color": "#94a3b8"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VScrollPanel",
|
||||
"id": "cockpit_chat",
|
||||
"options": {
|
||||
"css": "filler",
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"bgcolor": "#f8fafc",
|
||||
"border": "1px solid #e2e8f0",
|
||||
"borderRadius": "8px",
|
||||
"padding": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "选择一个迭代后,这里将显示对话记录。\n在下方输入框中描述你的需求,Agent 将自动分析并驱动产线执行。",
|
||||
"cfontsize": 0.9,
|
||||
"color": "#64748b",
|
||||
"padding": "20px"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "Form",
|
||||
"id": "chat_form",
|
||||
"options": {
|
||||
"name": "chat_form",
|
||||
"submit_url": "{{entire_url('/pipeline-sdlc/api/cockpit_chat.dspy')}}",
|
||||
"fields": [
|
||||
{"name": "iteration_id", "uitype": "hide", "value": ""},
|
||||
{"name": "task_id", "uitype": "hide", "value": ""},
|
||||
{"name": "message_text", "uitype": "textarea", "placeholder": "描述你的需求或回复Agent...", "rows": 3, "cwidth": 60},
|
||||
{"name": "action", "uitype": "hide", "value": "send_message"}
|
||||
]
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"padding": "8px 0 0 0",
|
||||
"gap": "8px",
|
||||
"justifyContent": "flex-end"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Button",
|
||||
"options": {
|
||||
"name": "send_btn",
|
||||
"label": "发送",
|
||||
"type": "submit",
|
||||
"css": "primary"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "beforesubmit",
|
||||
"actiontype": "script",
|
||||
"target": "self",
|
||||
"script": "var iter_id=bricks.getWidgetById('current_iteration_id');if(!iter_id||!iter_id.options.value){bricks.show_error({title:'提示',message:'请先在迭代管理中选择一个迭代'});return false;}var msg=this.getValue('message_text');if(!msg||!msg.trim()){bricks.show_error({title:'提示',message:'请输入内容'});return false;}return true;"
|
||||
},
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "submited",
|
||||
"actiontype": "script",
|
||||
"target": "self",
|
||||
"script": "this.setValue('message_text','');var c=bricks.getWidgetById('cockpit_chat');if(c)c.render({});var t=bricks.getWidgetById('cockpit_timeline');if(t)t.render({});"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user