pipeline-sdlc/wwwroot/api/my_todos_popup.dspy

141 lines
5.0 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# my_todos_popup.dspy - 我的待办列表(返回 bricks widget 描述,由 todo_badge.js 构建)
# 列表只做导航:每条待办点「查看内容并处理」→ todo_detail.dspy 渲染正文后再确认。
import json as _json
user_id = await get_user()
if not user_id:
return {"widgettype": "Message", "options": {"title": "未登录", "message": "请先登录后查看待办"}}
todos = await list_my_human_todos(user_id)
dbname = get_module_dbname('pipeline-sdlc')
TYPE_LABEL = {
'requirement_confirmation': '需求确认',
'design_confirmation': '设计确认',
'bug_acceptance': 'Bug 验收',
}
TYPE_COLOR = {
'requirement_confirmation': '#2563eb',
'design_confirmation': '#2563eb',
'bug_acceptance': '#f0a040',
}
detail_url = entire_url("/pipeline-sdlc/api/todo_detail.dspy")
def _s(v):
if v is None:
return ''
return str(v)
def _clip(s, n):
s = ' '.join(_s(s).split())
if len(s) > n:
return s[:n] + '…'
return s
# 项目名(一次查全,避免逐条查库)
proj_ids = []
for t in (todos or []):
pid = _s(t.get('project_id'))
if pid and pid not in proj_ids:
proj_ids.append(pid)
proj_names = {}
if proj_ids:
id_list = ','.join(["'" + p.replace("'", "") + "'" for p in proj_ids])
async with DBPools().sqlorContext(dbname) as sor:
precs = await sor.sqlExe("SELECT id, name FROM sd_projects WHERE id IN (" + id_list + ")", {})
await sor.sqlExe("COMMIT", {})
for r in (precs or []):
proj_names[_s(getattr(r, 'id', ''))] = _s(getattr(r, 'name', ''))
cards = []
for t in (todos or []):
src = _s(t.get('source'))
tid = _s(t.get('id'))
kind = 'question' if src == 'question' else 'human_task'
task_type = _s(t.get('task_type'))
if src == 'question':
badge = '待答问题'
badge_color = '#8b5cf6'
title = _clip(t.get('question'), 80) or '冒泡问题'
summary = '提问角色:' + (_s(t.get('from_role')) or '-') + ' | 类型:' + (_s(t.get('problem_type')) or '-')
else:
badge = TYPE_LABEL.get(task_type, '人类任务')
badge_color = TYPE_COLOR.get(task_type, '#2563eb')
title = _clip(t.get('title'), 80) or '待办任务'
summary = _clip(t.get('description'), 110)
pid = _s(t.get('project_id'))
meta_bits = []
pname = proj_names.get(pid) or _s(t.get('project_name')) or pid
if pname:
meta_bits.append('项目:' + pname)
if t.get('created_at'):
meta_bits.append(_s(t.get('created_at')))
open_script = ("var du=" + _json.dumps(detail_url) + "+'?kind=" + kind + "&id='+encodeURIComponent("
+ _json.dumps(tid) + ");"
"var rp=await fetch(du);var dd=await rp.json();"
"await bricks.widgetBuild(dd,bricks.app);")
sub = [
{
"widgettype": "HBox",
"options": {"width": "100%", "gap": "8px", "alignItems": "center"},
"subwidgets": [
{"widgettype": "Text",
"options": {"text": badge, "cfontsize": 0.7, "color": "#ffffff", "padding": "1px 8px",
"bgcolor": badge_color, "borderRadius": "10px", "whiteSpace": "nowrap"}},
{"widgettype": "Text",
"options": {"text": title, "cfontsize": 0.95, "color": "#0f172a"}}
]
}
]
if meta_bits:
sub.append({"widgettype": "Text",
"options": {"text": ' | '.join(meta_bits), "cfontsize": 0.7, "color": "#94a3b8"}})
if summary:
sub.append({"widgettype": "Text",
"options": {"text": summary, "cfontsize": 0.78, "color": "#475569", "width": "100%"}})
sub.append({
"widgettype": "HBox",
"options": {"width": "100%", "gap": "6px", "halign": "right"},
"subwidgets": [{
"widgettype": "Button",
"options": {"label": "查看内容并处理", "css": "primary small"},
"binds": [{"wid": "self", "event": "click", "actiontype": "script",
"target": "self", "script": open_script}]
}]
})
cards.append({
"widgettype": "VBox",
"options": {"width": "100%", "bgcolor": "#ffffff", "border": "1px solid #e2e8f0",
"borderRadius": "8px", "padding": "10px 14px", "gap": "6px",
"marginBottom": "6px"},
"subwidgets": sub
})
if not cards:
cards = [{"widgettype": "Text",
"options": {"text": "暂无待办", "cfontsize": 0.9, "color": "#94a3b8", "padding": "16px"}}]
return {
"widgettype": "PopupWindow",
"id": "my_todos_pw",
"options": {"title": "我的待办(" + str(len(todos or [])) + ")", "width": "76%", "height": "80%",
"auto_open": True, "resizable": True},
"subwidgets": [{
"widgettype": "VScrollPanel",
"options": {"css": "filler", "width": "100%", "height": "100%",
"padding": "12px", "gap": "8px"},
"subwidgets": cards
}]
}