feat(todo): 待办详情页——交付件/Bug/问题正文用 MdWidget+VScrollPanel 渲染,确认按钮与正文同屏(不再只给两个按钮)
This commit is contained in:
parent
790d8edb12
commit
21f104a19e
141
wwwroot/api/my_todos_popup.dspy
Normal file
141
wwwroot/api/my_todos_popup.dspy
Normal file
@ -0,0 +1,141 @@
|
||||
# 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",
|
||||
"style": {"background": badge_color, "borderRadius": "10px",
|
||||
"fontWeight": "bold", "whiteSpace": "nowrap"}}},
|
||||
{"widgettype": "Text",
|
||||
"options": {"text": title, "cfontsize": 0.9, "color": "#1e293b", "fontWeight": "bold"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
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
|
||||
}]
|
||||
}
|
||||
38
wwwroot/api/question_answer.dspy
Normal file
38
wwwroot/api/question_answer.dspy
Normal file
@ -0,0 +1,38 @@
|
||||
# question_answer.dspy - 我的待办里直接回答冒泡问题(回答后问题关闭、任务恢复执行)
|
||||
|
||||
import json as _json
|
||||
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return _json.dumps({"success": False, "error": "未登录"}, ensure_ascii=False)
|
||||
|
||||
question_id = ((params_kw or {}).get('question_id') or '').strip()
|
||||
answer = ((params_kw or {}).get('answer') or '').strip()
|
||||
|
||||
if not question_id:
|
||||
return _json.dumps({"success": False, "error": "缺少 question_id"}, ensure_ascii=False)
|
||||
if not answer:
|
||||
return _json.dumps({"success": False, "error": "回答内容不能为空"}, ensure_ascii=False)
|
||||
|
||||
res = await question_answer(question_id, answer, answered_by=user_id, answer_source='human')
|
||||
|
||||
ok = False
|
||||
msg = ''
|
||||
if isinstance(res, tuple) or isinstance(res, list):
|
||||
ok = bool(res[0]) if len(res) > 0 else False
|
||||
msg = str(res[1]) if len(res) > 1 else ''
|
||||
elif isinstance(res, dict):
|
||||
if 'success' in res:
|
||||
ok = bool(res.get('success'))
|
||||
elif 'ok' in res:
|
||||
ok = bool(res.get('ok'))
|
||||
else:
|
||||
ok = True
|
||||
msg = str(res.get('message') or res.get('error') or '')
|
||||
else:
|
||||
ok = bool(res)
|
||||
|
||||
if ok:
|
||||
return _json.dumps({"success": True, "message": msg or "回答已提交,问题关闭,任务恢复执行"},
|
||||
ensure_ascii=False)
|
||||
return _json.dumps({"success": False, "error": msg or "回答提交失败"}, ensure_ascii=False)
|
||||
353
wwwroot/api/todo_detail.dspy
Normal file
353
wwwroot/api/todo_detail.dspy
Normal file
@ -0,0 +1,353 @@
|
||||
# todo_detail.dspy - 待办详情:把要人类看的正文用 MdWidget 渲染(VScrollPanel 可滚动),
|
||||
# 操作按钮与正文一起给出(先看内容再确认,不再只给两个按钮)。
|
||||
# 入参:kind=human_task|question, id=<待办id>
|
||||
|
||||
import json as _json
|
||||
|
||||
user_id = await get_user()
|
||||
if not user_id:
|
||||
return {"widgettype": "Message", "options": {"title": "未登录", "message": "请先登录后查看待办"}}
|
||||
|
||||
kind = ((params_kw or {}).get('kind') or 'human_task').strip()
|
||||
oid = ((params_kw or {}).get('id') or '').strip()
|
||||
if not oid:
|
||||
return {"widgettype": "Message", "options": {"title": "打开失败", "message": "缺少待办 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',
|
||||
}
|
||||
|
||||
|
||||
def _s(v):
|
||||
if v is None:
|
||||
return ''
|
||||
return str(v)
|
||||
|
||||
|
||||
confirm_url = entire_url("/pipeline-sdlc/api/stage_confirm.dspy")
|
||||
bug_url = entire_url("/pipeline-sdlc/api/bug_accept.dspy")
|
||||
complete_url = entire_url("/pipeline-sdlc/api/human_task_complete.dspy")
|
||||
answer_url = entire_url("/pipeline-sdlc/api/question_answer.dspy")
|
||||
|
||||
# 处理成功:关详情弹窗 + 关列表弹窗 + 刷新角标
|
||||
_tail = ("if(d.success){"
|
||||
"var pw=bricks.getWidgetById('todo_detail_pw',bricks.app);if(pw){pw.destroy();}"
|
||||
"var lw=bricks.getWidgetById('my_todos_pw',bricks.app);if(lw){lw.destroy();}"
|
||||
"if(window.refreshTodo){window.refreshTodo();}"
|
||||
"var mo=new bricks.Message({title:'已处理',message:d.message||'处理成功'});mo.open();"
|
||||
"}else{var mf=new bricks.Message({title:'处理失败',message:d.error||'处理失败'});mf.open();}")
|
||||
|
||||
|
||||
def _read_input_js(wid):
|
||||
return ("var cw=bricks.getWidgetById(" + _json.dumps(wid) + ",bricks.app);var cv='';"
|
||||
"if(cw){cv=(typeof cw.resultValue==='function')?cw.resultValue():"
|
||||
"((cw.dom_element&&cw.dom_element.value)||'');}"
|
||||
"cv=(cv===null||cv===undefined)?'':String(cv).trim();")
|
||||
|
||||
|
||||
def _post_js(url, fields):
|
||||
js = "var body=new URLSearchParams();"
|
||||
for k, v in fields:
|
||||
js += "body.append(" + _json.dumps(k) + "," + v + ");"
|
||||
js += ("var rp=await fetch(" + _json.dumps(url) + ",{method:'POST',"
|
||||
"headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body});"
|
||||
"var d=await rp.json();")
|
||||
return js
|
||||
|
||||
|
||||
def _input_box(wid, label, tip):
|
||||
return {
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "gap": "4px", "padding": "8px 14px 0 14px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text",
|
||||
"options": {"text": label, "cfontsize": 0.75, "color": "#64748b"}},
|
||||
{"widgettype": "UiText", "id": wid,
|
||||
"options": {"name": wid, "width": "100%", "cfontsize": 0.85,
|
||||
"placeholder": tip, "height": "70px"}}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def _btn(label, css, script, conform=None):
|
||||
bind = {"wid": "self", "event": "click", "actiontype": "script",
|
||||
"target": "self", "script": script}
|
||||
if conform:
|
||||
bind["conform"] = conform
|
||||
return {"widgettype": "Button", "options": {"label": label, "css": css}, "binds": [bind]}
|
||||
|
||||
|
||||
title = ''
|
||||
badge = '待办'
|
||||
badge_color = '#64748b'
|
||||
sub_lines = []
|
||||
md = []
|
||||
action_widgets = []
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
project_id = ''
|
||||
if kind == 'question':
|
||||
qrecs = await sor.sqlExe(
|
||||
"SELECT id, tenant_id, task_id, from_role, question, problem_type, "
|
||||
"current_handler_role, created_at FROM pipeline_agent_questions "
|
||||
"WHERE id=${i}$", {"i": oid})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
if not qrecs:
|
||||
return {"widgettype": "Message", "options": {"title": "打开失败", "message": "问题不存在或已处理"}}
|
||||
q = qrecs[0]
|
||||
project_id = _s(getattr(q, 'tenant_id', ''))
|
||||
badge = '待答问题'
|
||||
badge_color = '#8b5cf6'
|
||||
qtext = _s(getattr(q, 'question', ''))
|
||||
title = qtext[:60] if qtext else '冒泡问题'
|
||||
sub_lines.append('提问角色:' + (_s(getattr(q, 'from_role', '')) or '-'))
|
||||
sub_lines.append('问题类型:' + (_s(getattr(q, 'problem_type', '')) or '-'))
|
||||
sub_lines.append('提问时间:' + _s(getattr(q, 'created_at', '')))
|
||||
md.append('## 问题内容')
|
||||
md.append('')
|
||||
md.append(qtext or '(无正文)')
|
||||
md.append('')
|
||||
rel_task = _s(getattr(q, 'task_id', ''))
|
||||
if rel_task:
|
||||
trecs = await sor.sqlExe("SELECT title, state FROM pipeline_tasks WHERE id=${t}$",
|
||||
{"t": rel_task})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
if trecs:
|
||||
md.append('## 关联任务')
|
||||
md.append('')
|
||||
md.append('- 任务:' + _s(getattr(trecs[0], 'title', '')))
|
||||
md.append('- 状态:' + _s(getattr(trecs[0], 'state', '')))
|
||||
md.append('')
|
||||
action_widgets.append(_input_box('todo_answer', '你的回答(回答后该问题关闭,任务自动恢复执行)',
|
||||
'请填写回答内容'))
|
||||
answer_script = (_read_input_js('todo_answer') +
|
||||
"if(!cv){var mn=new bricks.Message({title:'请填写回答',"
|
||||
"message:'回答内容不能为空'});mn.open();return;}" +
|
||||
_post_js(answer_url, [('question_id', _json.dumps(oid)), ('answer', 'cv')]) +
|
||||
_tail)
|
||||
action_widgets.append({
|
||||
"widgettype": "HBox",
|
||||
"options": {"width": "100%", "gap": "8px", "padding": "10px 14px", "halign": "right"},
|
||||
"subwidgets": [_btn('提交回答', 'primary', answer_script)]
|
||||
})
|
||||
else:
|
||||
hrecs = await sor.sqlExe(
|
||||
"SELECT id, project_id, iteration_id, bug_id, task_id, task_type, title, "
|
||||
"description, status, created_at, submitted_by FROM pipeline_human_tasks "
|
||||
"WHERE id=${i}$", {"i": oid})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
if not hrecs:
|
||||
return {"widgettype": "Message", "options": {"title": "打开失败", "message": "待办不存在或已处理"}}
|
||||
h = hrecs[0]
|
||||
task_type = _s(getattr(h, 'task_type', ''))
|
||||
project_id = _s(getattr(h, 'project_id', ''))
|
||||
title = _s(getattr(h, 'title', '')) or '待办'
|
||||
badge = TYPE_LABEL.get(task_type, '人类任务')
|
||||
badge_color = TYPE_COLOR.get(task_type, '#2563eb')
|
||||
sub_lines.append('创建时间:' + _s(getattr(h, 'created_at', '')))
|
||||
if _s(getattr(h, 'status', '')) != 'pending':
|
||||
sub_lines.append('状态:' + _s(getattr(h, 'status', '')))
|
||||
|
||||
desc = _s(getattr(h, 'description', ''))
|
||||
if desc:
|
||||
md.append('## 待办说明')
|
||||
md.append('')
|
||||
md.append(desc)
|
||||
md.append('')
|
||||
|
||||
src_task_id = _s(getattr(h, 'task_id', ''))
|
||||
bug_id = _s(getattr(h, 'bug_id', ''))
|
||||
|
||||
if task_type in ('requirement_confirmation', 'design_confirmation'):
|
||||
if src_task_id:
|
||||
trecs = await sor.sqlExe(
|
||||
"SELECT title, state FROM pipeline_tasks WHERE id=${t}$", {"t": src_task_id})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
if trecs:
|
||||
sub_lines.append('原任务:' + _s(getattr(trecs[0], 'title', '')))
|
||||
drecs = await sor.sqlExe(
|
||||
"SELECT title, deliverable_type, content, review_status, review_comment, "
|
||||
"created_by, created_at FROM pipeline_deliverables "
|
||||
"WHERE task_id=${t}$ ORDER BY created_at DESC LIMIT 5", {"t": src_task_id})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
else:
|
||||
drecs = []
|
||||
if drecs:
|
||||
d0 = drecs[0]
|
||||
md.append('## 待确认交付件:' + (_s(getattr(d0, 'title', '')) or '(未命名)'))
|
||||
md.append('')
|
||||
md.append('- 类型:' + (_s(getattr(d0, 'deliverable_type', '')) or '-')
|
||||
+ ' | 产出:' + (_s(getattr(d0, 'created_by', '')) or '-')
|
||||
+ ' | 时间:' + _s(getattr(d0, 'created_at', '')))
|
||||
rc = _s(getattr(d0, 'review_comment', ''))
|
||||
if rc:
|
||||
md.append('- PM 评审意见:' + rc)
|
||||
md.append('')
|
||||
md.append('---')
|
||||
md.append('')
|
||||
md.append(_s(getattr(d0, 'content', '')) or '(交付件正文为空,请到工作空间查看文件)')
|
||||
md.append('')
|
||||
if len(drecs) > 1:
|
||||
md.append('---')
|
||||
md.append('')
|
||||
md.append('## 历史版本')
|
||||
md.append('')
|
||||
for dx in drecs[1:]:
|
||||
md.append('- ' + _s(getattr(dx, 'created_at', '')) + ' | '
|
||||
+ (_s(getattr(dx, 'title', '')) or '-') + ' | '
|
||||
+ (_s(getattr(dx, 'review_status', '')) or '-'))
|
||||
md.append('')
|
||||
else:
|
||||
md.append('## 待确认内容')
|
||||
md.append('')
|
||||
md.append('未找到该任务的交付件记录,请到项目工作空间 deliverables 目录查看产出文件。')
|
||||
md.append('')
|
||||
|
||||
action_widgets.append(_input_box(
|
||||
'todo_comment', '修改意见(确认通过可留空;退回重做必须填写)', '请填写修改意见'))
|
||||
ok_script = (_read_input_js('todo_comment') +
|
||||
_post_js(confirm_url, [('human_task_id', _json.dumps(oid)),
|
||||
('confirmed', "'1'"), ('comment', 'cv')]) +
|
||||
_tail)
|
||||
no_script = (_read_input_js('todo_comment') +
|
||||
"if(!cv){var mn=new bricks.Message({title:'请填写修改意见',"
|
||||
"message:'退回重做前请说明需要修改什么'});mn.open();return;}" +
|
||||
_post_js(confirm_url, [('human_task_id', _json.dumps(oid)),
|
||||
('confirmed', "'0'"), ('comment', 'cv')]) +
|
||||
_tail)
|
||||
action_widgets.append({
|
||||
"widgettype": "HBox",
|
||||
"options": {"width": "100%", "gap": "8px", "padding": "10px 14px", "halign": "right"},
|
||||
"subwidgets": [
|
||||
_btn('确认通过', 'primary', ok_script,
|
||||
conform={"title": "确认通过", "message": "确认该阶段成果满足要求,继续后续任务?",
|
||||
"conform": {"label": "确认通过"}, "discard": {"label": "再看看"}}),
|
||||
_btn('不确定,退回重做', 'small', no_script)
|
||||
]
|
||||
})
|
||||
|
||||
elif task_type == 'bug_acceptance':
|
||||
if bug_id:
|
||||
brecs = await sor.sqlExe(
|
||||
"SELECT title, description, severity, priority, status, fix_description, "
|
||||
"fix_commit, assignee_id, created_at FROM sd_bugs WHERE id=${b}$", {"b": bug_id})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
else:
|
||||
brecs = []
|
||||
if brecs:
|
||||
b = brecs[0]
|
||||
md.append('## Bug:' + (_s(getattr(b, 'title', '')) or '(无标题)'))
|
||||
md.append('')
|
||||
md.append('- 严重程度:' + (_s(getattr(b, 'severity', '')) or '-')
|
||||
+ ' | 优先级:' + (_s(getattr(b, 'priority', '')) or '-')
|
||||
+ ' | 状态:' + (_s(getattr(b, 'status', '')) or '-'))
|
||||
md.append('- 处理人:' + (_s(getattr(b, 'assignee_id', '')) or '-')
|
||||
+ ' | 提交时间:' + _s(getattr(b, 'created_at', '')))
|
||||
md.append('')
|
||||
md.append('### 问题描述')
|
||||
md.append('')
|
||||
md.append(_s(getattr(b, 'description', '')) or '(无)')
|
||||
md.append('')
|
||||
md.append('### 修复说明')
|
||||
md.append('')
|
||||
md.append(_s(getattr(b, 'fix_description', '')) or '(开发未填写修复说明)')
|
||||
fc = _s(getattr(b, 'fix_commit', ''))
|
||||
if fc:
|
||||
md.append('')
|
||||
md.append('- 修复提交:`' + fc + '`')
|
||||
md.append('')
|
||||
else:
|
||||
md.append('## Bug 信息')
|
||||
md.append('')
|
||||
md.append('未找到关联 Bug 记录(bug_id=' + (bug_id or '空') + ')。')
|
||||
md.append('')
|
||||
ok_script = (_post_js(bug_url, [('bug_id', _json.dumps(bug_id)), ('accept', "'1'")]) + _tail)
|
||||
no_script = (_post_js(bug_url, [('bug_id', _json.dumps(bug_id)), ('accept', "'0'")]) + _tail)
|
||||
action_widgets.append({
|
||||
"widgettype": "HBox",
|
||||
"options": {"width": "100%", "gap": "8px", "padding": "12px 14px", "halign": "right"},
|
||||
"subwidgets": [
|
||||
_btn('验收通过(关闭 Bug)', 'primary', ok_script,
|
||||
conform={"title": "验收通过", "message": "确认该 Bug 已修复并关闭?",
|
||||
"conform": {"label": "验收通过"}, "discard": {"label": "取消"}}),
|
||||
_btn('不通过(重新打开)', 'small', no_script)
|
||||
]
|
||||
})
|
||||
|
||||
else:
|
||||
if not desc:
|
||||
md.append('## 待办说明')
|
||||
md.append('')
|
||||
md.append('(无说明)')
|
||||
md.append('')
|
||||
action_widgets.append(_input_box('todo_result', '处理结果(提交后进入 QC 检查)', '请填写处理结果'))
|
||||
do_script = (_read_input_js('todo_result') +
|
||||
"if(!cv){var mn=new bricks.Message({title:'请填写处理结果',"
|
||||
"message:'处理结果不能为空'});mn.open();return;}"
|
||||
"var rd=JSON.stringify({content:cv});" +
|
||||
_post_js(complete_url, [('human_task_id', _json.dumps(oid)),
|
||||
('result_data', 'rd')]) +
|
||||
_tail)
|
||||
action_widgets.append({
|
||||
"widgettype": "HBox",
|
||||
"options": {"width": "100%", "gap": "8px", "padding": "10px 14px", "halign": "right"},
|
||||
"subwidgets": [_btn('提交处理结果', 'primary', do_script)]
|
||||
})
|
||||
|
||||
# 项目名
|
||||
if project_id:
|
||||
precs = await sor.sqlExe("SELECT name FROM sd_projects WHERE id=${p}$", {"p": project_id})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
if precs:
|
||||
sub_lines.insert(0, '项目:' + (_s(getattr(precs[0], 'name', '')) or project_id))
|
||||
|
||||
head_widgets = [{
|
||||
"widgettype": "HBox",
|
||||
"options": {"width": "100%", "gap": "8px", "alignItems": "center", "padding": "12px 14px 4px 14px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text",
|
||||
"options": {"text": badge, "cfontsize": 0.75, "color": "#ffffff", "padding": "2px 10px",
|
||||
"style": {"background": badge_color, "borderRadius": "10px", "fontWeight": "bold",
|
||||
"whiteSpace": "nowrap"}}},
|
||||
{"widgettype": "Text",
|
||||
"options": {"text": title, "cfontsize": 1.0, "color": "#1e293b", "fontWeight": "bold"}}
|
||||
]
|
||||
}]
|
||||
if sub_lines:
|
||||
head_widgets.append({
|
||||
"widgettype": "Text",
|
||||
"options": {"text": ' | '.join(sub_lines), "cfontsize": 0.75, "color": "#94a3b8",
|
||||
"padding": "0 14px 6px 14px", "width": "100%"}
|
||||
})
|
||||
|
||||
return {
|
||||
"widgettype": "PopupWindow",
|
||||
"id": "todo_detail_pw",
|
||||
"options": {"title": badge + " - " + title[:40], "width": "86%", "height": "86%",
|
||||
"auto_open": True, "resizable": True},
|
||||
"subwidgets": [{
|
||||
"widgettype": "VBox",
|
||||
"options": {"css": "filler", "width": "100%", "height": "100%", "gap": "0px"},
|
||||
"subwidgets": head_widgets + [
|
||||
{
|
||||
"widgettype": "VScrollPanel",
|
||||
"options": {"css": "filler", "width": "100%", "padding": "0 14px",
|
||||
"style": {"background": "#ffffff", "border": "1px solid #e2e8f0",
|
||||
"borderRadius": "6px"}},
|
||||
"subwidgets": [{
|
||||
"widgettype": "MdWidget",
|
||||
"options": {"mdtext": '\n'.join(md), "width": "100%", "padding": "6px 10px"}
|
||||
}]
|
||||
}
|
||||
] + action_widgets
|
||||
}]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user