192 lines
7.6 KiB
Plaintext
192 lines
7.6 KiB
Plaintext
# 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 验收',
|
||
'flow_plan_confirm': '流程裁剪确认',
|
||
'tech_template_confirm': '技术方案骨架确认',
|
||
'bid_delivery_confirm': '交付确认',
|
||
'material_supply': '实体材料缺口',
|
||
}
|
||
TYPE_COLOR = {
|
||
'requirement_confirmation': '#2563eb',
|
||
'design_confirmation': '#2563eb',
|
||
'bug_acceptance': '#f0a040',
|
||
'flow_plan_confirm': '#7c3aed',
|
||
'tech_template_confirm': '#0891b2',
|
||
'bid_delivery_confirm': '#059669',
|
||
'material_supply': '#d97706',
|
||
}
|
||
|
||
detail_url = entire_url("/pipeline-sdlc/api/todo_detail.dspy")
|
||
ticket_detail_url = entire_url("/ticket/api/ticket_detail_popup.dspy")
|
||
wallet_detail_url = entire_url("/pipeline-llm/api/llm_wallet_todo_popup.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 == 'ticket':
|
||
# 工单待办(2026-09-10 接入平台待办):badge 用 provider 给的分类
|
||
badge = _s(t.get('badge')) or '工单'
|
||
badge_color = '#0ea5e9'
|
||
title = _clip(t.get('title'), 80) or '工单'
|
||
summary = _clip(t.get('description'), 110)
|
||
elif src == 'llm_wallet':
|
||
# 模型账号钱包余额不足(2026-09-11 接入平台待办):状态派生,充值到账自动消失
|
||
badge = _s(t.get('badge')) or '钱包充值'
|
||
badge_color = '#dc2626'
|
||
title = _clip(t.get('title'), 80) or '钱包充值'
|
||
summary = _clip(t.get('description'), 110)
|
||
elif 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 ('(项目已删除)' if pid else '')
|
||
if pname:
|
||
meta_bits.append('项目:' + pname)
|
||
if t.get('created_at'):
|
||
meta_bits.append(_s(t.get('created_at')))
|
||
|
||
if src == 'ticket':
|
||
# 工单待办详情走 ticket 模块自持端点(from_todo=1 → 弹窗 id 用待办专用,
|
||
# 关闭时连带关待办列表弹窗)
|
||
open_script = ("var du=" + _json.dumps(ticket_detail_url) + "+'?from_todo=1&id='+encodeURIComponent("
|
||
+ _json.dumps(tid) + ");"
|
||
"var rp=await fetch(du);var dd=await rp.json();"
|
||
"await bricks.widgetBuild(dd,bricks.app);")
|
||
elif src == 'llm_wallet':
|
||
# 钱包待办详情走 pipeline-llm 自持端点(弹窗内直接充值)
|
||
open_script = ("var du=" + _json.dumps(wallet_detail_url) + "+'?id='+encodeURIComponent("
|
||
+ _json.dumps(tid) + ");"
|
||
"var rp=await fetch(du);var dd=await rp.json();"
|
||
"await bricks.widgetBuild(dd,bricks.app);")
|
||
else:
|
||
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": "var(--ink-0)"}}
|
||
]
|
||
}
|
||
]
|
||
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": "var(--ink-3)", "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": {"otext": "暂无待办", "text": "暂无待办", "i18n": True, "cfontsize": 0.9, "color": "#94a3b8", "padding": "16px"}}]
|
||
|
||
# embed=1(手机端「待办」Tab 整页渲染):返回纯 VBox,不套 PopupWindow 弹窗壳
|
||
_embed = ((params_kw or {}).get('embed') or '').strip()
|
||
if _embed == '1':
|
||
return {
|
||
"widgettype": "VBox",
|
||
"options": {"css": "filler", "width": "100%", "height": "100%",
|
||
"padding": "0", "gap": "0px"},
|
||
"subwidgets": [{
|
||
"widgettype": "VScrollPanel",
|
||
"options": {"css": "filler", "width": "100%", "height": "100%",
|
||
"padding": "12px", "gap": "8px"},
|
||
"subwidgets": cards
|
||
}]
|
||
}
|
||
|
||
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
|
||
}]
|
||
}
|